websites playing timing roulette
Saturday, March 7, 2009 12:01:03 AM

You might think that nothing newsworthy was happening on the planet. Unfortunately it was not the start of a new and peaceful world order, it was merely their JavaScript playing timing roulette:
if (typeof callback == 'function') {
if (document.addEventListener) {
window.setTimeout(function(){
document.write('<script type="text/javascript" charset="utf-8">(' + callback.toString() + ')();<\/script>');
}, 0)
}
What is that doing? This code says "have a 0 millisecond break, then add this SCRIPT tag to the document". (By "0 millisecond" it sort of means "as soon as you get around to it".) The problem with that is that if the browser happens to complete loading the document before running that document.write() statement it will replace the current document with a single, invisible SCRIPT tag.
Opera, doing its best at trying to show you the page as soon as possible did finish loading the document before getting back to the timeout - so the page was overwritten and disappeared.
Now, by the above it sounds like it is a drawback to be fast (again?). Wait until you see an extract of some code that broke image upload on Orkut:
<head>
<script>setTimeout( function(){ document.body.appendChild(el) }, 0 )</script>
<script>/*other stuff here*/</script>
</head><body>
In this case, if the timeout runs before we've seen the BODY tag - it will break, because no document.body exists yet. Wait, at New York Times parsing quickly was a mistake and here..we're punished because we didn't get to BODY yet when running the timeout?? So we're simultaneously too fast for NYTimes and too slow for Orkut..
This is JavaScript timing roulette - such things happen when websites are written according to the timing of specific browsers, or even the speed of the network connection the web site developer uses! What would happen if you're visiting Orkut on a really slow connection and the network has a small hiccup-pause between HEAD and BODY? By Orkut's code I would not be surprised if certain network delays - only a few milliseconds - would break the site entirely.
And it gets worse. AOL sites often try to launch slideshows in popup windows. In Opera that doesn't go very well:
AOL is another gambler addicted to playing the timing roulette. Bear with me, because this gets complex - but they have something like
<SCRIPT src="http://www.aolcdn.com/ke/swfobject/ke_kit_popup_includes.js" type="text/javascript" language="javascript" charset="utf-8"></SCRIPT>
<DIV id="gallery-holder">
<DIV id="news-news_popup_foobar">
The popup_includes.js contains code that appends external scripts to HEAD. The final external scripts (ke_kit_refresh.js) contains further inline code that calls rederPopupPage() which calls a method in the opener window that eventually calls the embedswf() method which starts looking for the element with the ID "news-news_popup_foobar" (where 'foobar' is actually the short name for the article).
Since loading scripts block parsing, we have not yet reached the DIV when the slideshow is supposed to be inserted. So when the script asks "do you have the element news-news_popup_foobar?" Opera responds "no, not yet anyway" and the script just gives up.
So, apparently we're supposed to keep parsing forward while we're running scripts dynamically added earlier in the DOM?! The W3C never told us that, I think. It's the web's dark matter striking again.









Andrew Gregory # Saturday, March 7, 2009 2:58:10 AM
BTW, *should* document.write ever overwrite the current document? I thought it always added to the document and that document.open was the function that wiped out the document?
Anonymous # Saturday, March 7, 2009 3:49:59 AM
Anonymous # Saturday, March 7, 2009 4:01:13 AM
Anonymous # Saturday, March 7, 2009 4:28:53 AM
Anonymous # Saturday, March 7, 2009 4:33:37 AM
Anonymous # Saturday, March 7, 2009 7:31:41 AM
edvakf # Saturday, March 7, 2009 8:00:17 AM
By the way, Firefox and Safari (in their beta versions) implemented Speculative loading, which loads external scripts and cache them until the proper script tag appears. It makes a browser far robust against "network hiccups" and, imho, it isn't hard for Opera as well to implement it.
When will we see it..?
Anonymous # Saturday, March 7, 2009 10:24:46 AM
Andrew Gregory # Saturday, March 7, 2009 1:32:14 PM
As for document.write, it's always nice to see one function doing two different things at different times. I don't *want* to know which idiot thought of that one
Charles SchlossChas4 # Saturday, March 7, 2009 3:41:38 PM
Safari does use site-specfic hacks, I am not sure about FireFox but my guess is something simalar. So they also have trouble.
Omega JuniorOmegaJunior # Saturday, March 7, 2009 4:22:14 PM
Anonymous # Saturday, March 7, 2009 8:03:06 PM
Hallvord R. M. Steenhallvors # Saturday, March 7, 2009 11:31:07 PM
I think it is a bit of both. The final issue is probably an unwritten rule - it's a bit counter-intuitive that if you have a document with
SCRIPT id=a
SCRIPT id=b
DIV id=c
in the markup, neither script can see the DIV. However, if you have
SCRIPT id=b
DIV id=c
and "script b" contains code that inserts "script a" through the DOM, script a should see the DIV. You end up with exactly the same DOM structure yet "script a" runs when the DOM is in a different state, and in the AOL page's case behaves very differently.
NYTimes fixed it quite quickly - I don't think we even got around to complaining to them about it. Pretty good turnaround time when they needed to fix a mistake
@edvakf and Andrew Gregory: delayed script execution is indeed similar to the "speculative loading" - though perhaps a little more ambitious. If it's bug-free enough and improves perceived performance (which it logically should) it would be nice to enable it by default
The larger point in this article is that writing code that depends on a certain non-specified, implicit timing of events is a bad idea. Period. Do you want a site that breaks if a browser improves its parsing and DOM performance? Do you want a site that breaks if the TCP packet containing BODY start tag is a little bit delayed by a slow network? Of course not.
Charles SchlossChas4 # Sunday, March 8, 2009 2:44:43 AM
Anonymous # Monday, March 9, 2009 6:48:40 AM
Anonymous # Monday, March 9, 2009 12:22:11 PM
Anonymous # Monday, March 9, 2009 12:23:55 PM
Hallvord R. M. Steenhallvors # Monday, March 9, 2009 3:11:56 PM
Well, Mr. anonymous, the usual drill: make sure bugs are reported, analysed, given sufficiently high priority, write the test cases, suggest politely to webmasters that these sites might want to move their SCRIPT elements below the elements they rely on, write browser.js patches that make the sites work even if they depend on undocumented magic timing luck, regression test the fixes when developers write them, remove browser.js patches if the fixes work, and blog about any interesting observations in the process. If there is anything else you think I should do about it, I'm all ears.
Charles SchlossChas4 # Monday, March 9, 2009 4:16:13 PM
http://www.aolfeedback-clarabridge.com/se.ashx?s=04BD76CC65A7BB3C&tid=19&r=http%3A%2F%2Fwww.aolhealth.com%2Fhealth%2Fodds-of-disease&ch=us.health&oid=&c=en-US
on the AOL glitch
Hallvord R. M. Steenhallvors # Monday, March 9, 2009 5:17:54 PM
FataL # Monday, March 9, 2009 8:50:46 PM
http://my.opera.com/community/forums/topic.dml?id=266662
http://globeprgroup.com/tests/opera-bugs/javascript-jump-to-anchor/
I think those bugs is pretty relevant to a post...
Zotlan # Tuesday, March 10, 2009 11:15:07 AM
Anonymous # Wednesday, March 11, 2009 1:44:24 PM
FataL # Wednesday, March 11, 2009 5:51:50 PM
Anonymous # Thursday, March 12, 2009 1:26:35 PM
Anonymous # Thursday, March 12, 2009 5:53:07 PM
Omega JuniorOmegaJunior # Saturday, March 14, 2009 9:11:17 AM
This is quite true. I've worked in the field of software programming for over 15 years. Anyone working in any field any length of time, sees new people coming in and making the same mistakes.
This is a problem of lack in education. Usually, this problem starts with the project manager.
edvakf # Saturday, March 14, 2009 7:39:44 PM
Apologies for the off-topic again.
The Delayed Script Execution works similarly to the Speculative Loading. After all, however, it's a functionality for a mobile browser as far as I feel. Yes it's turned on in the Opera Turbo edition that was just unveiled, but I can easily imagine a script (both page script and user script) not working when it's turned on.
One obvious thing I found was that Opera with Delayed Script Execution won't fire DOMContentLoaded event any more. I'm quite sure it will affect browser.js behavior as well.
Since it is apparently only Opera that doens't already implement Speculative Loading among the 5 major browsers, I strongly hope Opera either to refine the Delayed Script Execution to be trouble free, or to have the Speculative Loading.
http://stevesouders.com/ua/
Just a thought anyway.
Hallvord R. M. Steenhallvors # Tuesday, March 17, 2009 9:35:02 AM
first of all: when I tested with a simple TC, Opera seems to send DOMContentLoaded also when delayed script execution is enabled. If you see such problems, could you point me to a page where this occurs?
(The test I used was as simple as putting this snippet in a HTML file:
document.addEventListener('DOMContentLoaded', function(){ passed=true; }, false)so there may well be scenarios this test didn't catch where it fails)
Secondly, enabling delayed script execution should not fix the AOL problem I mention, actually. This is because the implementation of delayed execution goes to great lengths to make sure the script that runs after parsing sees the DOM as it would be if the script ran normally. Doing things differently caused some problems - one example I remember was a reload loop on milonic.com where a frame breaker checking top.frames.length was confused by an IFRAME after the script. Since top.frames.length was larger than expected the script tried to "break out" of the presumed frameset.. So the intention behind delayed execution is that enabling it should make no difference whatsoever from the running script's point of view.
Hallvord R. M. Steenhallvors # Tuesday, March 17, 2009 9:42:43 AM
edvakf # Tuesday, March 17, 2009 10:47:33 PM
I tested this UserJS.
// ==UserScript== // @include http://* // ==/UserScript== document.addEventListener('DOMContentLoaded',function(){opera.postError(0);},false) document.addEventListener('DOMContentLoaded',function(){opera.postError(1);},true) window.addEventListener('DOMContentLoaded',function(){opera.postError(2);},false) window.addEventListener('DOMContentLoaded',function(){opera.postError(3);},false) opera.addEventListener('BeforeEvent.DOMContentLoaded',function(){opera.postError(4);},false) opera.addEventListener('BeforeEvent.DOMContentLoaded',function(){opera.postError(5);},true) opera.addEventListener('AfterEvent.DOMContentLoaded',function(){opera.postError(6);},false) opera.addEventListener('AfterEvent.DOMContentLoaded',function(){opera.postError(7);},true)WITHOUT Delayed Script Execution turned on, the console messages are below;
on both http://portal.opera.com/upgrade/ and http://www.google.ca/
Then I turned on the DSE, I get ABSOLUTELY NO messages on http://portal.opera.com/upgrade/, but exactly the same message as above on http://www.google.ca/
I'm on Opera 9.64, by the way.
PS. I'm completely for the idea that websites should not rely on specific browsers' implementation. I was just wondering if Opera was left behind from the crowd.
Hallvord R. M. Steenhallvors # Friday, March 20, 2009 9:29:22 PM
Kamaleshkamalesh # Tuesday, April 28, 2009 8:49:52 PM
Btw, I'm trying out "delayed script execution" in the lates Opera alpha build, and it seems to a bit faster... Will keep playing with it and give feedback...
Hallvord R. M. Steenhallvors # Tuesday, May 12, 2009 9:27:57 PM