The double-padding/nowrap bug: how to make IE6 hit 100% CPU usage with some simple HTML + CSS

I came across an amusing bug in IE6 last week. The existence of a bug in IE is no great surprise, but the way it manifests itself is quite interesting: with just a tiny bit of HTML and CSS, you can cause IE’s CPU usage to spike up to 100% and stay there, slowly leaking away memory. It seems unlikely that this has never been seen before, (especially as it doesn’t occur in the IE7 beta), but I couldn’t find a reference to it anywhere on the web, so I’m posting it here.

It starts off with a simple piece of HTML:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
            "http://www.w3.org/TR/html4/strict.dtd"><html>
<head>
   <title>IE6 100% CPU test page</title>
</head>

<body>
   <table>
      <tr>
         <td>
            <p><span class="test">abcd efgh ijkl mnop</span></p>
         </td>
      </tr>
   </table>
</body>
</html>

Especially in table-based layouts, it’s not unusual to see a span wrapped in a p wrapped in a table cell. The problem kicks in, though, when you apply the following CSS:

<style type="text/css">
   p {
      padding:0.5em;
      position:relative;
      white-space:nowrap;
   }
   span.test {
      padding:1em;
   }
</style>

Setting the position of the p is potentially unusual, and you might wonder why the white-space:nowrap is being applied to the paragraph rather than the span, but at least on the surface, it all looks kosher. Nuh-uh.

I’ve set up a test page with exactly this code in it. Try using IE6 to visit it. Does everything seem to be working as normal? How about if you try resizing your window to narrow it down…and down…until it’s just wide enough to hold the text. Oh no! It’s dead. Poor IE.

Now imagine the text in the cell being wider, or the table having several of those cells in a row, so that even at a normal window size the CPU usage spikes as soon as you load the page. Major bumcraft. This was a pig to track down and debug.

But even having reduced the problem to a simple test case, I’m still not sure why this should go wrong. It looks like IE’s rendering model is unable to resolve a circular reference between the p and the span when the forced width of the nowrap and the added paddings interact. But beyond that…mmmidunno. As always, it pays to be on your guard when dealing with IE and padding.

Update (20 Feb 2006): After playing around with this bug a bit more, I’ve found that it’s even worse than I’d first described. You don’t even need the p to be embedded in a table cell to bring IE down. Using the same CSS as above, the following HTML is sufficient (example 2):

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
            "http://www.w3.org/TR/html4/strict.dtd"><html>
<head>
   <title>IE6 100% CPU test page</title>
</head>

<body>
   <p><span class="test">abcd efgh ijkl mnop</span></p>
</body>
</html>

You don’t even have to use a p and a nested span, either. Although I haven’t tested every possible combination, it looks like any inline element nested within a block-level element shows the same behaviour, e.g. an em within a h3 or an a inside a div. If you nest a block-level element inside another block (e.g. a p inside a div, everything’s fine.

What’s even more amusing is if you remove the DTD from the HTML above, and watch what happens (example 3). IE still goes to 100% cpu, but it retains just enough spare capacity to refresh its display. This time, if you narrow the window down, the text disappears, and the window’s vertical scrollbar makes it look like the page has got enormously tall. But if you try scrolling up and down, the content is nowhere to be found. If fact, it looks like IE is still trying to figure out where the content should go, too: if you scroll part-way down the (blank) page, you’ll notice the scroll block jumping up and down like a confused monkey.

But then if you try to widen the window back to its original size, it freezes up completely again.

And yes, it also works if you place the style definitions inline, rather than in a <style> block (example 4):

<html>
<head>
   <title>IE6 100% CPU test page</title>
</head>

<body>
      <p style="padding:0.5em;position:relative;white-space:nowrap"><a style="padding:0.3em" 
href="http://www.example.com">abcd efgh ijkl mnop.</a></p>
</body>
</html>

If you needed another reason why it’s a really bad idea to allow visitors to use HTML in your blog comments section, well, there you go.

Wiggle stereoscopy, follow-up

Almost immediately after putting up my posting about Wiggle stereoscopy with Javascript, I got reports of funny behaviour with Internet Explorer. I can’t say this came as a great surprise.

To recap on the technique, the core idea was to:

  1. replace a composite image with a <div> of a fixed size,
  2. set the background-image of this div to be the same as the replaced image, and
  3. flip the position of the background image every 120ms

Replacing the composite image with a div

Toggling the background image position

You can see the original script in action in Example 1.

The problem lies with the way Internet Explorer handles CSS background images. If IE’s cache settings are at their least forgiving (i.e. check for newer versions of stored pages “Every visit to the page”), changing the background-position of the background image causes IE to requery the web server for the image in question. The answer will generally be “HTTP 304: Not Modified”, so at least it doesn’t re-download the image again, but there’s a good chance that 120ms (the flicker rate) is not long enough for the server round-trip to complete. Result: IE spends all of its time checking for a new version of the image, and no time at all actually displaying anything.

Internet Explorer set to check for a newer version of a page on every visit

This problem is comprehensively documented at FiveSevenSix.com. There is a workaround involving server settings, but that goes against my desire for a keeping the technique nice and simple to implement. Alternatively, I could probably have used a variant of the double-buffering technique to fix it. But upon reflection I wondered if it was worth using background images at all.

Background images are perfect if you want to transform a piece of naked markup (like a <ul> list) into something graphically pleasing. But the wiggle technique is specifically designed to animate an existing in-line image. So: the new version of the script (1.1) does the following:

  1. It wraps a <div> around the <img>,
  2. sets the overflow property of the <div> to hidden, so that the excess parts of the image are masked off, and
  3. changes to position of the <img> itself every 120ms.

Example 2 is running with version 1.1 of the script.

Another problem that the original wiggle script had was that even with IE’s cache settings at a more normal level, it interfered with the mouse hovering over any hyperlinks in the same page: the hand cursor would change immediately back to an arrow, and in some configurations the url of the hovered link would also instantly disappear from the status bar. Version 1.1 fixes this, too.