Skip navigation.

miscoded

the web is a hack

Posts tagged with "coding"

Onestat.com's browser sniffer older than my son

, , ,

Seems the boss has been browsing web stats again - I came across a bug report from him saying a DHTML menu on onestat.com only works once. (Load page, click any "demo" link at the bottom and try the menu on the left.)

I'm not paying that much attention to stats myself, being too busy doing the work that will hopefully smoothen out compatibility problems and make it possible to grow our usage share. It's exactly the type of brokenness Jon found on onestat.com I fear the most - a bit hidden away, subtle, annoying when the user needs that page to work but perhaps not disastrous enough to notify us of. Too many of these issues, and we've lost a user! (Unless, of course, the user happens to be our CEO :smile: - luckily he's an active surfer who comes across more compat problems than any average user and eagerly reports them :wink:).

Now, deep inside the Onestat scripts is some code to handle browser differences. The script assumes that a browser below a certain level of DOM support can't be expected to create menus after the document loaded. That's of course not a bad assumption to make - until you see the sniffing they use to determine whether you have sufficient DOM support. Here is the relevant part:
if(kh.indexOf("Opera/7")>-1||kh.indexOf("Opera 7")>-1)return "Op7"; 
. 
. 
else if(kh.indexOf("Opera")>-1)return "Default";


Check the calendar, sir: we're in late 2009. Opera 7 - the only Opera version this script thinks is capable of opening the menu a second time - was released in January 2003. That's a 6 year old browser sniffer! I hope the script that generates their statistics is a bit more up to date!

And it's more than a little ironic that a web statistics company contributes to Opera's low usage share by running such old and broken code on their website. :frown:

'ESPN FLASH detection system' meets Flash 0

, , , ...

Even in the classy company of bad version detection scripts we've met since we started testing Opera 10, this Flash detection approach stands out. That script goes to great effort to require an update every.single.time Adobe releases another Flash version.

It starts with a bold claim to be the ESPN "Flash detection system", no less:

// Author: Danny Mavromatis
// Version: 2.07.0
// Created: 10/29/2001
// Updated: 3/6/2006
// ESPN.com FLASH detection system
var f2 = false;
var f3 = false;
var f4 = false;
var f5 = false;
var f6 = false;
var f7 = false;
var f8 = false;
var f9 = false;

Look at all those variables. What might they be used for? Read on:

var fD = navigator.plugins["Shockwave Flash" + isVersion2].description;
var fV = parseInt(fD.charAt(fD.indexOf(".") - 1));

So, first it extracts one single letter that precedes a dot in the plugin's description of itself. This is presumably the plugin's major version number, you know the 8 in "8.0", the 9 in "9.0", and, um, the 0 in "10.0". Then:
f2 = fV == 2;
f3 = fV == 3;
f4 = fV == 4;
f5 = fV == 5;
f6 = fV == 6;
f7 = fV == 7;
f8 = fV == 8;
f9 = fV == 9;


..it just sets the corresponding "f"-variable to true. If the major version was 8, f8 will be true and so on. A great way to make sure the code will require maintenance - new variables for each new version. And then comes the real gem:

for (var i = 2; i <= mV; i++) {
if (eval("f" + i) == true) aV = i;
}
// alert("version detected: " + aV);



Let's see, we just had a variable fV which contained Flash's major version number (or at least its least significant digit) - but this script has severe amnesia. What was that number again? Better use a loop and 8 eval() calls to check the value of that variable. You never know, do you?

Now, dear readers - Mr. Mavromatis clearly needs some help with this code. Obviously, complexity and maintenance requirements are among his design goals. The natural question is whether there are good, non-obvious ways this script could be improved to be more complex and require even more maintenance? Suggestions welcome in comments.

Update: Danny Mavromatis responded in comments and - though he no longer works on the ESPN site - has made sure that the page where we found this problem has been updated. Kudos to Mr. Mavromatis for his quick response and sense of responsibility. I wish more web developers would act this way!

No more personal attacks, please!

the day supporting document.onload became a bug

,

Like many things on the web, the load event seems simple. It originates from the web's stone age - supported by Netscape 2 in 1996 and fired billions and billions of times since. It's probably the most widely used event for web scripting. Can it have secrets or quirks after so many years?

You might think you could write a one-sentence spec for it, along the lines of "when the document is done loading, dispatch a load event to any listeners created from BODY onload attributes, window.onload property or addEventListener('load') calls" - and implement it interoperably in a few hours on top of an otherwise solid DOM/JS core.

Simplicity is deception. Actually, the cool thing about the load event is that it seems so simple but is so weird.

Discovering what I didn't know about the load event started at the white "please wait" box that covers the contents on grainger.com (this site is now patched in browser.js)



I noticed that they did something like
document.onload=function(){
// code that shows the "please wait" box, an obvious mistake
}
.
.
<body onload="">


Aha: if you assign a listener for the document's load event but afterwards have an empty onload attribute on body, document.onload will be removed when the browser sees onload="". Right? I wrote a test case confirming that assumption and moved on to the next bug.

However, colleague Ola was also looking at the bug and could prove that the onload="" was a red herring. Removing it made no difference. Assigning anything to document.onload was a no-op in all other browsers. What?? How come we never noticed that?

So, we got a developer to remove document.onload support. I wanted it fixed by just ignoring assignments to document.onload, but being a thorough person Daniel made sure document would no longer be an event target for the load event.

To my consolation I wasn't the only person saying that must be wrong! This fix makes document.addEventListener('load'...) a no-op too. We're absolutely sure document.addEventListener('load'...) must keep working. The DOM Events spec doesn't say much about the load event, but it sort of implies sending it to the document. Besides, not sending load events to document anymore broke opera:config and dozens of browser.js patches.

Then we wrote another test case or two.

And it's true. No browser (except Opera) sends load events to "document"! They all skip document and go straight for window. BODY onload="" creates listeners on window, not on body. Even though event.target is the document, the event itself avoids the document like it had swine flu or something.

I assume it's been like that since 1996 and we never thought of testing that in other browsers before. Wow.

So, early afternoon on May 12th 2009, our support for document.onload turned from a feature into a bug. Good bye to the possibility of capturing all load events inside a document with a capturing listener (won't work with window.addEventListener because Mozilla turned out to be a chicken and not a dinosaur). Edit: luckily I was wrong here, see comments.

Actually it feels a bit .. evil .. to suddenly turn around and kill an old feature that's been with us for years, just because it's required for web compatibility. Oh well. At least I can confirm that you learn something old every day..

websites playing timing roulette

, ,

For a couple of days in November, the New York Times looked serenely white in Opera:


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.

Taleo - The Leader in On Demand Unbalanced Quotes

, , ,

Load getacooljob.com in Opera, see error page (http://aac.taleo.net/servlets/CareerSection?art_ip_action=FlowDispatcher&flowTypeNo=13&pageSeq=1&art_servlet_language=en&csNo=2'%3E%3C/head%3E%3Cbody%20bgcolor=). At first sight it might look like a standard browser sniffer saying "go away and don't dare becoming our customer until you've found a browser we like"... until you look a bit closer at that address. See the junk at the end?

&csNo=2'%3E%3C/head%3E%3Cbody%20bgcolor= decodes to
&csNo=2'></head><body bgcolor=
and it's caused by the lack of a closing quote for the META tag's content attribute on the previous page:

<META HTTP-EQUIV="refresh" content="01; url='http://aac.recruitsoft.com/servlets/CareerSection?art_ip_action=FlowDispatcher&amp;flowTypeNo=13&amp;pageSeq=1&amp;art_servlet_language=en&amp;csNo=2'>


The site apparently belongs to Taleo which is the "Leader in On Demand Talent Management". As they aren't talented enough to balance their quotes or use a validator to spot markup mistakes, the level of hype obviously exceeds their technical competence..

See also statements like "this Career Section does not allow the use of the BACK or FORWARD browser buttons during the application process" (source), the hideous sniffing here
function stopEvent() {
var userAgent = window.navigator.userAgent;
var appName = window.navigator.appName;
if ((appName.indexOf("Explorer") >= 0) && (userAgent.indexOf("Mozilla/3") >= 0) && (userAgent.indexOf("Mac") >= 0)) {
return (true);
} else {
return (false);
}
}

and their "anti-multiple-tabs" JavaScript
//---------------------------------------------------------------------
// This method increments the count of browser using the career section
// on the current session in the document's cookie.
// This code is inserted only when IE browser is used
//---------------------------------------------------------------------
function setMultiBrowserDetection()
{
if (window.navigator.cookieEnabled)
{
// The multibrowser problem doesn't occur where cookies are not available
var cs_cnt = -1;
var index = document.cookie.indexOf("cs_cnt");
var countbegin = -1;
var countend = -1;
if (index == -1)
{
cs_cnt = 1;
}
else
{
countbegin = (document.cookie.indexOf("=", index) + 1);
countend = document.cookie.indexOf(";", index);
if (countend == -1)
{
countend = document.cookie.length;
cs_cnt = eval(document.cookie.substring(countbegin, countend));
}
else
{
if (countend < countbegin)
{
cs_cnt = 0;
}
else
{
cs_cnt = eval(document.cookie.substring(countbegin, countend));
}
}
if (cs_cnt < 1)
{
cs_cnt = 1;
}
else
{
cs_cnt = cs_cnt + 1;
}
}
document.cookie = "cs_cnt="+cs_cnt;
if (cs_cnt > 1)
{
// More than one browser is using the current session
window.document.location = "http://aac.taleo.net/servlets/CareerSection?art_ip_action=MultipleBrowserConflict";
}
}
}
complete with eval() instead of parseInt() (meaning that anyone who can get a cookie set on this site can XSS them).. When you stop shaking your head, I'd like you to opine on how much less talented a web site could possibly get..

shortest incompatible script challenge

, ,

We all know that the interoperability situation on the Web has been abysmal. Web developers everywhere are vocally voicing their complaints of wasted time and money due to browser differences. Standards bodies that are meant to solve the interoperability problem become battle grounds of special interests, or create ivoryish spec-monsters that end up fragmenting the web even more. The dazzling bells and whistles of plugins threaten open standards..

So, let's have some incompatibility fun.

Here's a challenge for JavaScript-skilled readers: Who can come up with the shortest possible JavaScript that produces 4 different results in the top 4 engines?

(For the purpose of this exercise let's define the top 4 browsers as IE, Firefox, Safari/WebKit and Opera - latest available final versions).

The winner gets fame and fortune. (Well, I think I can afford a reward of ISK1000 these days if anyone is interested - certainly our friends in Iceland really need someone wanting to buy their money.. p: So the grand price is a souvenir 1000 krónar bill from Jon's country of origin.)

Ladies and gentlemen, post your answers in the comments.

How to cook tag soup with XSLT

,

Working for Opera Software's QA department gives you in-depth perspectives on the web's wild and varied coding practises. I still wasn't prepared for the curious solutions that power the menu on the new Israeli rail website.

The XSLT markup/programming language is widely used to transform one sort of DOM into another - for example turning the DOM of a generic XML file into valid XHTML. Much of the benefit is that you're working on DOM trees - making it hard or impossible to create syntactically invalid pages.

Diving into the source code shows that the JavaScript coders working on the Rail site were asleep during their education's "what's the point of XSLT" lesson. The coding is unbelievable. It's more like an XML parser/serializer stress test than a production site. Now, I don't really know XSLT and trying to debug this confirms my impression that it must be one of the worse programming languages mankind has invented - but the point of this script is to generate HTML with XSLT *string concatenation*?!?? Look at this:

<xsl:value-of select="$attribute-name"/>="<xsl:call-template name="inner-attribute-text-value"><xsl:with-param name="attribute-value" select="$attribute-value"/></xsl:call-template>"

or

<xsl:template name="inner-text-tag-open"><xsl:text disable-output-escaping="yes"><</xsl:text></xsl:template>
<xsl:template name="inner-text-element-close">
<xsl:param name="element-name"/><xsl:call-template name="inner-text-tag-open"/>/<xsl:value-of select="$element-name"/><xsl:call-template name="inner-text-tag-close"/></xsl:template>
<xsl:template name="inner-text-tag-close"><xsl:text disable-output-escaping="yes">></xsl:text></xsl:template>


Yes, all that to create a text node containing e.g.
</div>
in a DOM they will serialize only to parse it again by setting innerHTML on some poor element..

When they in their wisdom chose to generate markup inside text nodes with their XSLT they run into the familiar problem: when is < going to start a tag and when is it going to live in a text node? Hence, < is sometimes escaped as an 'lt' entity to create proper text nodes with HTML source-as-text in them (see for example the instance of
&lt;
in the code above). Now, of course when they set innerHTML they do not want this entity to appear as a literal < so they do some pre-processing: all entities they want to change into proper < and > before setting innerHTML have a comment node next to them:

<!--nwlt-->&lt;TR class="nw-2r"&gt;<!--nwgt--><!--nwlt-->&lt;TD class="nw-2c"&gt;<!--nwgt-->


and their pre-processing is a simple string replace:

sHtml = sHtml.replace(/\<!--nwlt--\>&lt;/g,"<").replace(/&gt;\<!--nwgt--\>/g,">").replace(/\<[\/]?tbody\>/gi,"");


(Why they hate the poor TBODY so much they must strip it from the markup even though the browser will re-generate them in the DOM as soon as innerHTML is parsed I can't even begin to imagine.)

If you thought XML-based toolchains and processes were going to make the Web a saner place, think again. We have now seen that in the right hands, XSLT is just another recipe for tag soup.

Hotmail has class

, ,

Hotmail's choice of class attribute for its HTML element if you use Opera to access it just strikes me as very funny.. The output of a
javascript:alert(document.documentElement.className)
command is:



That's right. In other words
<html class="Firefox FF_Win FF_M1 FF_D5 Opera">
Is that what you would call exquisite confusion? :confused:

Aside, I hope to have the basic Hotmail working again in Opera 9.2x before the end of the day. After all, what's an insulting class declaration to a browser from the proudly egalitarian Scandinavian peninsula? We're above class, for sure p: