miscoded

the web is a hack

Subscribe to RSS feed

Posts tagged with "ie"

Don't look to IE anymore..

, ,

I've been looking at a bug reported for the Chinese site 56.com (what's up with China and all those numerical domain names - lucky numbers smile?) Here's a screenshot from a colleague, Opera on the left and Chrome on the right:


Digging a bit, it boils down to a failure in a piece of JavaScript that reads innerHTML of an element and does a regular expression match() that ends up matching nothing. They use a regular expression created from this string:

var sRepeat="<%begin_"+xLev+"[^>]*%>((.|\\n)+?)<%end_"+xLev+"%>"; 


and it's a problem with how the line endings are encoded. The site's code contains typical Windows line endings (newline + carriage returns) but this regular expression assumes that innerHTML will contain newlines and not carriage returns. (This is the \\n part of the regexp. The fix is simply replacing "\\n" with "\\n|\\r".)

Firefox and Chrome return innerHTML with normalized LF line endings. IE and Opera return CRLF in this case, and comments are broken both in IE (8) and Opera for me.

Wait..did I just say that? We break a shiny, mass-market production site because we follow IE closely on a piece of JavaScript IE invented and defined? And IE is broken too??

Wow! The times they are a-changing..

New adventures in compatibility testing

, , ,

I'm having some fun trying to figure out how sites use document.getElementsByName(), and thought some of you might be interested in the testing approach.

The bug I'm investigating is a small and ugly one hiding in the document.getElementsByName() implementation - getElementsByName('someID') will find an element with id="someID".

This is of course bad behaviour. That method has nothing to do with IDs and should find elements by name only.

The good news is that it's trivial to fix. The bad news is that it's there for a reason, and the reason is called Internet Explorer. We've been bug-compatible on purpose and while we'd like to remove the bug we have no idea how many sites will break if we do!

So, I'd like an answer to questions like these:
  • How many sites use getElementsByName() to find elements with an ID?
  • Do these sites break if we fix the bug?
  • Do they have alternate code paths for browsers doing it right? If yes, how do they figure out what code to use?

Tools at our disposal: the MAMA web code search engine (an internal Opera project), User JavaScript, and two ad-hoc Opera Unite services.

MAMA tracks sites that might be using document.getElementsByName(). It knows about roughly 45 000 sites where it has seen the string "getElementsByName" in script source code, and it generously provides 5000 random ones in a text file on my request. Naturally, MAMA does only static analysis of the scripts, it can't tell whether the method is actually called or what it was used for.

That information is a piece of cake to get with User JavaScript. A trivial custom script, trackGEBNabuse.js, overwrites the getElementsByName() method with one that will do a bit of debugging and logging on our behalf. And I'm playing with Opera Unite for the first time, with one logging service and one URL player that keeps track of which of the 5000 URLs were already visited and sends Opera to the next one.

(Opera Unite actually rocks! It's fun to write backend-type logic in JavaScript rather than PHP, and it's less hassle while developing to keep all the information, URL lists, log files and scripts locally on the hard drive. I've been undecided about Unite, not sure if it was more important than all the other things we should be spending time on - now I see it's maturing and making itself useful. Nice.)

To walk you through the main logic of things - here's the user JS that overwrites the native method to do logging - commented:

(function(gebn){/* "gebn" is a reference to the actual, native function */
	document.getElementsByName=function(name){ /* overwrite the real one */
		var elementList=gebn.apply(this, arguments); /* call the native function, record the list it returns */
		/* we want to know if anything in the elementList is there due to a matching id rather than a matching name */
		var abuse=[];
		for(var i=0,elm;elm=elementList[i];i++){ /* go through all returned elements */
			if( elm.getAttribute('name')!==name )abuse.push(elm.outerHTML); /* we found one that's probably in the list because of an ID attribute! */
		}
		if(abuse.length>0){
			/* log errors to some server... */
			(new Image()).src='http://hr-opera.hallvors.operaunite.com/logger/logGEBN/?data='+encodeURIComponent(abuse.join(', '))+'&href='+encodeURIComponent(location.href);
		}
		return elementList;/* don't forget to return the list of elements to the waiting script */
	}
})(document.getElementsByName); /* this is where we pass the real method as an argument to the function */

As you see, it uses the oldest trick in the book - new Image() - to ping the Unite service with some data. The data is then stored in the folder I told Opera Unite to use when installing the widget.

The only other interesting part is the code that requests the next URL from the URL player - as trivial as doing this from a load event listener:
	if(location.hostname!='hr-opera.hallvors.operaunite.com')
		setTimeout( function(){ location.href='http://hr-opera.hallvors.operaunite.com/urldriver/nexturl?'+Math.random(); }, 500 );

The urldriver service also accepts the "urllist=somefile.txt" query string argument, so a different user scripts could play URLs from a different file (though not at the same time since the index of what URL one has reached is not stored per-file. That's obviously a bug in my Unite service - keep in mind that these are ad-hoc throwaway services done in 30 minutes of cutting, pasting and typing last night, so don't expect QA and polish :-p).

And the results? Left an Opera 10.10 instance to surf on its own in 5 different tabs overnight, which generated this log file listing 6 unique sites and the HTML of the elements returned in response to a getElementsByName() call due to this bug. Analysing 6 out of 5000 URLs manually is certainly doable smile. I'm still worried about getElementsByName() usage that only happens during user interaction, but now at least we know that 0.12% of the sites out there might be at risk from any change and we have some real code to look at. And automated analysis of websites is a new and interesting use case for User JavaScript.

X-Talisman-Compatible: messup

, , ,

Yikes.

I'm seeing X-UA-Compatible abuse everywhere these days. It seems most of the big sites I look at have decided not to give the IE8 team half a chance to iron out their bugs, they've all simply decided to close their eyes and slap on a "behave like IE7" instruction instead. Guess if this is going to cause compat problems down the road when all non-IE browsers are forced to look at the META tags and figure out if they should be bug-compatible with IE7, 8 or 9 for any given site.. :-(

Here's a funny offender: adobe.com insists that if I'm going to show their "flashAbout_info_small.swf" animation correctly, then ..

GET /swf/software/flash/about/flashAbout_info_small.swf HTTP/1.0
Host: www.adobe.com
Referer: http://www.adobe.com/products/flash/about/

HTTP/1.1 200 OK
X-UA-Compatible: IE=7
Content-Type: application/x-shockwave-flash

Date: Sat, 21 Feb 2009 23:39:33 GMT
Content-Length: 594

... I have to render the .swf like IE7 would.

No problems, Adobe. Since I work for a browser vendor the server's wish should be my command. Here's your about Flash SWF rendered by IE in IE7 compat mode:



Beautifully compatible, no? Thanks for the X-UA-Compatible hint, I would never have figured that out on my own!

Microsoft listens

, ,

IE allows a semicolon between if block and else

,

Just for the brainparsers our there: A semicolon between a closing curly bracket of an if block and the following else keyword only works in IE:

ie-only-if-else.htm

Should this work or would you consider such JS code broken beyond repair?

quick spec for IEs document.activeElement

,

document.activeElement is an IE thing. Opera supports it partially, and this is basically what one would need to do to match IE's behaviour (after some very quick and cursory testing):

  • when the document is loaded, before any interaction activeElement is the body element (!)
  • activeElement is set after mousedown.
  • it is set to the event's target if it is "focusable" (A, INPUT, BUTTON etc.), otherwise it is set to the event's target's .offsetParent
  • it keeps pointing to the same element until another interaction with the document sets it again


It is also set when you tab through links and form fields. In Opera, spatial navigation and other keyboard navigation should of course set it too.

Haven't checked if the WHATWG or WebAPI are already embracing and/or extending it..

real *nix devs don't test in IE

, ,

Over at basket.kde.org they are understandably Windows-averse. I guess their website is not exactly targeted at IE users, but even so some cross-browser testing would uncover bugs and errors in their JS. Snippet:

    // Cross-browser implementation of element.addEventListener()
    function addEventListener(element, type, expression, bubbling) {
     bubbling = bubbling || false;
     if (window.addEventListener) { // Standard
      element.addEventListener(type, expression, bubbling);
      return true;
     } else if (window.attachEvent) { // IE
      element.attachEvent('on' + type, expression);
      return true;
     } else return false;
    }


Whoever wrote that obviously is confused about event bubbling versus event capturing (luckily it defaults to a sensible false!) but the main problem with this code is the line

if (window.addEventListener) { // Standard


Um, no, what you are seeing there is not the W3C standardised window.addEventListener1. You're actually checking for the existence of this very function - the one we're inside when we hit that statement. Naturally IE chokes on the next line and no event handlers are added. (If you ask, it should read if(element.addEventListener).)

So - a slick, good-looking production site that wasn't tested with IE - what a rarity! p

Edit - note 1: well, actually W3C didn't specify addEventListener for window in the first place, it remains a Gecko extension like I've complained about earlier so the comment "// Standard" is doubly wrong..

Microsoft against VBScript plugin sniffers!

, , , ...

Imagine a browser that doesn't support VBScript. Think of a site using VBScript to detect Flash. Think of users being annoyed when they are told their browser "does not have Flash" even though it does. Sounds familiar?

Well, this time we're not talking Opera! We're dealing with PIE - Pocket IE on Pocket PC... They've done some testing over there and realised that VBScript Flash detection is quite common. Hence, Microsoft now tells you to avoid using VBScript plugin detection as it hurts interoperability!!


LOL. Priceless. And thanks for the URL, malware.