Skip navigation.

miscoded

the web is a hack

STICKY POST

Introduction

My journal is Opera-related and technical. It will cover the main obstacles we come across when we use Opera on the Web as it is - the standard violations, the browser incompatibilities, the sniffers and faulty scripts. That is the whole mess a poor browser has to make sense of and believe me, Opera is doing a brilliant job.

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:

browser.js update: eBay, Sun webmail, Salesforce

A new browser.js file is out, and like last time I'll post a changeblog with some background information. (I might spin these posts off to a separate blog at some point but for now it's here.)

First, some headlines:
  • Sun System Messenger Express webmail fix.
  • eBay.fr will no longer hang
  • Finding a doctor in South Carolina becomes easier, thanks to fearphage
  • Removed patch for maps.live.com - reborn at Bing maps
  • Conflict between Salesforce and WebForms2


Sun System Messenger Express webmail fix.



Opera 10 aligns the policy for setting document.domain with the other browsers, and require both pages to set document.domain before allowing communication. (In other words, if www.example.com wants to talk to example.com both of them must set document.domain to 'example.com'. In the old implementation, only the content from www.example.com would have to do so).

Normally, aligning with the other browsers shouldn't cause problems - but unfortunately, I've noticed that certain sites use browser sniffing before setting document.domain! I have no idea why.

For example, Facebook uses this oddity to avoid setting document.domain in certain older Firefox versions:

if (navigator && navigator.userAgent && document.domain.toLowerCase().match(/(^|\.)facebook\..*/) && !(parseInt((/Gecko\/([0-9]+)/.exec(navigator.userAgent) || []).pop(), 10) <= 20060508)) { document.domain = window.location.hostname.replace(/^.*(facebook\..*)$/i, '$1'); }


I wish I knew why it's such a bad idea to set document.domain in Firefox-versions released before May 8, 2006.. :sherlock:

Similarly, a webmail suite from Sun which is used by big universities and such contains this sniffing:

var agt=navigator.userAgent.toLowerCase();

var is_nav  = ((agt.indexOf('mozilla')!=-1) && (agt.indexOf('spoofer')==-1)
            && (agt.indexOf('compatible') == -1) && (agt.indexOf('opera')==-1)
            && (agt.indexOf('webtv')==-1) && (agt.indexOf('hotjava')==-1));
var is_gecko = (agt.indexOf('gecko') != -1);

if(is_nav || is_gecko) document.domain = document.domain
//document.domain = document.domain


and they run right into the new security policy when the sniffing means they avoid setting document.domain and all sorts of things break.

To solve this, each time we're about to run a script named setdomain.js or load a file named sample_lr.html - on any website - Opera will first append "Gecko" to its navigator.userAgent string. Oh, the weird and whacky things you need to do for compatibility.

eBay.fr will no longer hang



eBay.fr uses the SELECT.remove() method, but with element nodes instead of numbers as arguments. Their spec violation, our problem. This will be fixed in core, to allow using remove() for OPTION nodes like other browsers do - meanwhile, browser.js will sort it out for eBay.

Finding a doctor in South Carolina becomes easier


Do I have any readers in South Carolina? Thanks to fearphage's neat emulation of IE's bugfeature which lets you find a named form element with document.getElementById(), you can now use Blue Cross Doctor and Hospital Finder even though it relies on IE's violations against the DOM standard. The patch has been waiting while we were trying to get through to someone at Blue Cross Blue Shield who might be able to fix it, time is up - and big thanks to fearphage!

Removed patch for maps.live.com



Now maps.live is Bing Maps. No point in applying patches for the old hostname anymore.

Conflict between Salesforce and WebForms2 / HTML5



A report wizard inside Salesforce.com fails in Opera because of our support for the data attribute from WebForms2.

When they do

<select id="typeSelector" name="type" onchange="fillSelectFromArray(document.report_select.rep, ((this.selectedIndex == -1) ? null : data[document.getElementById('typeSelector').selectedIndex]));" title="Report Type Category"> 


"data" doesn't refer to the global variable data but to the data property on the SELECT object due to the scope of the event handler.

This attribute has since been removed from HTML5 so Opera will drop it at some point. Meanwhile, another stopgap site patch makes Salesforce work.

The 9sky.com fix in the previous edition was also about a problem caused by HTML5. Standards development and experimental implementations is obviously a major compatibility risk. If we want to improve the web's underlying technologies there isn't any other way forward, though.

browser.js updates: Hotmail, Tuenti, AOL Webmail

,

Quick overview of browser.js updates during the last couple of weeks.

Read more...

Most expensive javascript ever?

, , ,

I've wanted to tell this story for a while, and I don't think I'm spilling any beans or disclosing any sensitive information at this point.

So, a while ago Opera Software needed more servers. Not just a few servers either - we were planning Opera Mini's growth, implementing Opera Link, and My Opera was also growing quickly. We predicted crazy server load increases for the foreseeable future (and man, were we right!)

Clearly we needed to make a massive investment on the server capacity front (basically buying these shiny things and then some.)

Management put a hefty check on the table - I'm sure our beloved sysadmins felt like kids before Christmas - and salivating sales people from major hardware vendors grabbed our requirements spec, dived into their CRMs and crunched their spreadsheets. They emerged with offers and sample servers shipped all the way to Oslo for our testing pleasure.

However, one of the world's biggest hardware vendors - whose name every single reader will be familiar with, and whose hardware a good share of you will be using right now - apparently didn't do their homework. When Opera's sysadmin booted up the server to test its web-based administration interface, they came across a single JavaScript statement that managed to piss off everyone up to and including the CTO.

This single statement, apparently written by some sub-contractor they had outsourced admin interface programming to, cost them millions of NOK in lost sales.

And the code they sent all the way to Oslo for testing? Here's an extract:

if (is.opera)
{
window.location.href="config/error.htm";
}

Facebook monitors your alert() usage

,

If you use a bookmarklet on Facebook and it calls window.alert(), it doesn't quite do what you expect. They've re-defined the entire alert() method - it will pop up a box, but it will also behind the scenes send what you tried to pop up to the server!?! Look at Facebook's alert code (shown in an appropriate setting, of course):



Since I routinely use alert() for debugging, should I be paranoid now?
I really wonder what this feature is intended for and whether they actually harvest this data and use it for anything.

'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..