The Long, Hard Haul to Uncovering a Single, Tiny CSS Property Bug in IE10 | Caktus Group
The Long, Hard Haul to Uncovering a Single, Tiny CSS Property Bug in IE10

There’s a very small but devastatingly crash-inducing bug in Internet Explorer 10. Watch out for setting a background-color to inherit on any pseudo element (like ::before and ::after), because this will crash IE completely every single time.

#element::after {
    background-color: inherit; /* CRASH! */
}

Rather than dedicate an entire post to a single CSS property, let’s trace the work that was required to track this down. The payoff is obviously important (a supported browser crashing is a big problem!) but it can feel frustrating when a big issue comes down to something so seemingly tiny. It is important to remember the real measure of the bug is not the number of lines involved in fixing it (one, in our case) but the amount of effort it took to track down. Don’t let the small fix (just give it a color directly) betray the hard work you put into understanding the issue.

First, how did we get here, anyway? A crashing browser is something you hope doesn’t go unnoticed for long.

It is pretty common to try to avoid running full regression tests across a site, both early on and throughout the project. When you have a dozen supported browsers on the low end, the most efficient workflow tends to be getting a lot of feature work and layout under your belt early, and then attacking each browser’s quirks after testing what you’ve built across them. If you try to test the entire supported set of features for every small changeset, especially on a pre-launch product, you’re going to be repeating a lot of work and sinking a lot of hours you could better spend getting more of the initial work wrapped up.

So a couple days of iteration had been done with new feature work and a few bug fixes, and then we found this crash that perplexed us. At that point, it could have been any one of a dozen commits across a number of components. We could have expected some tweaks would be needed to bring Safari and Internet Explorer in line, but the outright crash certainly took us by surprise. We began down a road to narrow the confirmation. We ruled out SauceLabs, the excellent browser testing service, by reproducing it locally on our own setup. IE 11 didn’t crash, and most of the site was just fine except this one page, so we knew it had to be a pretty specific issue.

We had enough to start tracking it down. Being able to reproduce it reliably and only on one browser meant we could really focus any tests we did, so we got started right away.

Having confirmed this is reproducible, how do we start to narrow this down when we aren’t sure when it happened and the crashing nature of the bug precludes us from looking into what’s going on?

While the problem happened on a single page, that wasn’t enough to go on to tell us where it was happening. We had not even discovered that it was a CSS issue at this point, so basically anything was suspect.

We turned to the ever useful Git Bisect command, which every developer should have in their toolbelt. Git Bisect, if you haven’t had an opportunity to learn about it yet, helps you locate a specific commit in your history that caused a bug. You start by telling it the last known commit where you can confirm the bug did not exist and the latest commit (usually the head of your branch or master) where the bug does exist. Git begins narrowing it down, basically stepping halfway between the two known good and bad commits and at each step asking you “What about this commit, is it good or bad?” At the end of the process, which should only take less than half a dozen steps in most cases, you’ll have a single commit where the problem first appears. This is great when you have a problem that went undetected for a while and you need a smaller target to aim for.

The diff in question is CSS only, so now we know the problem is in our CSS! This really helped to narrow it down, both to one aspect of our changes (neither markup nor Javascript seemed to be at fault) and in the number of changes to look closer at (it was not a very large diff). It was, however, much more surprising than a strange Javascript bug might have been. Who ever heard of CSS crashing a browser, after all? The first thought was a parser bug and we poured over the diff looking for errant curly braces or other oddities that might have gone unnoticed and caused this crash. Everything looked flawless, from a syntax perspective, so we were left with the conclusion that it was some actual style causing the failure, rather than a parser issue.

As both a sanity check and a confirmation of our findings so far, we cleared the entire CSS file where these changes were isolated to and pulled it back up in Internet Explorer 10. This confirmed our findings when a very ugly, but non-crash-inducing, version of the page was rendered. We now had clear confirmation that something in this simple CSS file was the culprit, and a quick look at the short commit we had narrowed down as the time when the problem was introduced gave us no good candidates.

There were a few color changes. A bit more padding on one element and a slimmer margin on another. Heck, several of the changes were just fixing indentation in a few places! Nothing stood out.

One by one we commented out the rulesets that had been changed in the diff until we found one that only caused the problem when it was present:

#cssmenu::after {
    position: absolute;
    top: 0;
    left: -50%;
    width: 150vw;
    height: 100%;
    content: "";
    background-color: inherit;
    z-index: -1;
}

This is a standard trick to create a wider background padding around a section that needed to expand beyond the content well. It is just a simple pseudo element with all the necessary properties. These pseudo elements require the position, top, left, width, height, and content properties. This leaves the final two properties, background-color and z-index, as likely causes.

Finding the answer was background-color was only a matter of commenting them each out one by one to test, and seeing this combination from the start of the article was our problem all along:

#cssmenu::after {
    ...
    background-color: inherit;

We had the problem and immediately knew the solution was easy. The color being inherited was just “white” and we had only used “inherit” to remove a minor redundancy. Explicitly stating the color here fixed the problem immediately.

But, it fixed the problem in seconds, while it had taken us nearly an hour to track down the bug between two of us. We had to know more about what had caused us so much headache and followed up with a few experiments.

We had a theory and a false-positive test that this affected ::after but not ::before elements, and quickly learned this affected both varieties of pseudo element.

We dug around as much as we could without sinking time, but could not find any mention of this problem in the usual places people complain about things (StackOverflow, MSDN forums).

If you’ve found this post because you googled the terms “css after background-color inherit crash” then I hope you can breathe a sigh of relief and move on with your day.

For the reader following this story for the journey and not the solution, remember this the next time you feel betrayed by a long debug session that reveals what seems like a small issue: you did not waste your time. The measure of a bug should not be in lines of code or the time to fix the problem once you understand it, but in the effort it requires to uncover, understand, and correct. If we let ourselves be deceived by what false measurements of how big or difficult a bug really is, focusing too much on only the very final and sometimes minor step of fixing it, we let ourselves devalue the very important and difficult work of uncovering complicated problems. And that is where real skill and experience lies, not in the few lines at most it might take to correct.

Download Shipping Faster: Django Team Improvements
blog comments powered by Disqus