Sharovatov’s Weblog

Efficient IE version targeting

Posted in browsers, css, javascript, web-development by sharovatov on 3 June 2009

When you’re writing a web page, you have to keep in mind that IE6 and IE7 have higher market share than all other browsers combined and IE8 has only started gaining popularity. So in most projects you have to support IE6 and IE7, even though IE6 support for CSS2.1 has a significant amount of bugs and issues. IE7 was slightly better and IE8 provides full CSS2.1 support, which is nice.

Some web-developers whose fanatic hatred to IE blows out common sense, propose really weird “solutions”, while all sane people support all their target audience browsers with significant market share.

So we have three IE versions with different level of CSS support. One of the most often mentioned issues is IE6’s lack of support for :hover pseudo class on elements other than links. IE7 and IE8 support :hover fully. Another sample is that IE8 supports :after and :before generated content elements while IE6 and IE7 don’t. These are just two samples that spring to my mind just to show that level of CSS2.1 support really differs.

The first thing that most web-developers would use to specify a CSS rule for different browsers would be CSS hacks. But this leads to problems. For example, when IE7 wasn’t yet published, and people were using IE6, many developers used star selector bug to fix CSS2.1 issues in IE. When IE7 was shipped, it fixed support for star hack, but didn’t fix all the CSS2.1 issues. So using CSS hacks is perfectly backwards compatible – you know in which already shipped browsers and their versions it works – but is not future-compatible. This is because CSS hacks do not provide an obvious mechanism for a version targeting.

Conditional comments to rescue

Microsoft was aware that this problem would occur and in IE5 (and all newer versions, of course) they included conditional comments feature. There’s a perfect PPK’s post about “conditional comments” with samples so I’m not going to dive into details of conditional comments in this post. The main thing is that you can specify a version vector (IE version) and serve different IE versions with separate content – usually, CSS files.

Conditional comments allow to configure compatibility.

I consider a best practice to set one separate CSS and JS file for Internet Explorers that are older than IE8:

<!--[if lte IE 7]> <link rel="stylesheet" type="text/css" href="iefix.css"> <![endif]-->

 

And inside this iefix.css I use star selector hack to fix issues in IE6 and lower:

* html #someElement { /*rules for IE6 and lower */ }

Using star selector hack here is a perfect way to target IE6 and lower:

  1. we are aware that star selector CSS hack was fixed in IE7
  2. we are inside a CSS file that is only served to IE7 and lower

It’s perfectly future-compatible (IE8 and newer versions won’t fetch it as it’s inside a conditional comments) and also perfectly backwards-compatible (we know that star selector hack works in IE6 and lower versions).

So there’s no real need for a separate CSS file for IE6 and lower because we can easily separate IE7 and lower versions’ rules inside our iefix.css file.

Version targeting in javascript

There are cases when adding some javascript for IE6 and lower versions is required, for example, if you want to emulate :hover support on some elements.

Microsoft thought about this and along with conditional comments provided a conditional compilation technique. The approach is very similar – you put some javascript code inside /*@cc_on @*/ comment block and IE parses it. To control which version to target, special conditional compilation variable @_jscript_version is provided. This variable shows the build number of JScript compiler. Different versions of IE had different JScript compiler versions: IE5.01 had @_jscript_version of 5.1, IE5.5 – 5.5, IE6 – 5.6, IE7 – 5.7 and IE8 – 5.8. And this approach worked fine until Windows XP Service Pack 3 was shipped which replaced IE6’s JScript compiler of version 5.6 with a newer one of version 5.7. Before SP3 you could use the following code to separate IE6 from IE7 and upper versions:

 /*@cc_on 
   if (@_jscript_version < 5.7) {
     //code for IE6 and lower
   } else {
     //code for IE7 and upper
   }
 @*/

But when SP3 was installed, IE6 JScript compiler was updated and wouldn’t enter the first if clause. This was a serious compatibility issue and many libraries had to update their code to find a workaround. The problem was that we were solving an issue of one technology (CSS) with another (javascript), and these technologies are supposed to be loosely coupled. So JScript was upgraded, new features were added, but CSS wasn’t. This leads me to a conclusion that

Using conditional compilation version targeting to solve CSS problems is wrong

Again, these technologies are loosely coupled and you cannot assume that if @_jscript_version is X, CSS parser version is Y.

So ideally, if you want to support :hover for IE6 and lower in javascript, serve a separate javascript file for them using conditional comments.

But if want to support something that’s not provided in a specific version of JScript compiler – using conditional compilation is perfectly valid.

P.S. Well, if you still want to separate all IE6 from IE7, here’s a snippet that would work:

/*@cc_on
if (@_jscript_version==5.6 ||
   (@_jscript_version==5.7 && 
      navigator.userAgent.toLowerCase().indexOf("msie 6.") != -1)) {
  //ie6 code
}
@*/

So even if Windows XP user installed Service Pack 3 which updated JScript compiler to version 5.7, IE6 own version will still be 6. And it’s perfectly safe to use userAgent sniffing inside a conditional comments block which will be run in IE only.

Share :

Advertisements

Universal Internet Explorer 6 CSS

Posted in browsers, css, web-development by sharovatov on 22 May 2009

Today I’ve read an interesting article by Andy Clarke here.

Here’s the author’s key points:

  1. IE6 is hard to support
  2. it’s so cool to write CSS for modern browsers only
  3. sites shouldn’t look the same in different browsers
  4. people come to websites for content

So by throwing these thoughts in he’s making a conclusion that IE6 should be supported in a limited way – i.e. by giving it a special “stripped” CSS

So let’s see.

IE6 is hard to support

From technical side it’s not hard, period. The amount of effort depends on the project peculiarities (to a degree).

It’s cool to write CSS for modern browsers only

Doing your work is not always doing cool stuff.

Sites shouldn’t look the same in different browsers

It fully depends on the project peculiarities.

People come to websites for content

It fully depends on the project peculiarities.

I do keep saying that all this depends on the project peculiarities.

And I can’t understand why it’s not obvious to Andy Clarke. When a project is planned, all the issues are taken into consideration. As websites are displayed in different browsers, three key factors matter:

  1. nature of the website
  2. market share of the browser
  3. cost of supporting a browser

Do I need to explain in more detail?

P.S. Yes I think people should upgrade from IE6. But not because it has an outdated support for standards, but just because newer browsers provide much better user experience.

P.P.S. You know, Andy, I really appreciate what you’re doing. I urge you not to stop. I’ll be priced higher for the same skills that I have now just because you (and those whom you manage to convince) deliberately choose not to deliver what clients need :)

Share :

IE8 Rendering Modes theory and practice

Posted in browsers, css, http, IE7, IE8, web-development by sharovatov on 18 May 2009

Many web-developers moan about the Rendering Modes switch that’s been introduced in IE8. I’ve got some thoughts on this.

Rendering modes switches history

As you may know, IE5 on Mac (followed by IE6 on Windows) introduced Quirks and Standards compliancy modes with a DOCTYPE switcher. All other browsers followed the move and introduced similar approach – they tried to mimic IE behaviour in quirks mode and tried supporting CSS as close to the W3C standard as they could.

DOCTYPE switch approach was really straight forward – if you put a valid DOCTYPE for your HTML page, browser switches to the standards compliancy mode; if DOCTYPE is omitted, quirks mode is used.

This switch was required because many sites were built at the time when even CSS1 was a draft, and for browsers to evolve safely (and add newer standards’ support) without breaking the existing web these old websites were to be rendered properly.

At the time the idea of using DOCTYPE as a rendering mode switcher was fine – almost all pages that had been built without standards support in mind didn’t have DOCTYPE. And if developers wanted to use modern standards, they added HTML or XHTML DOCTYPE and the page was rendered using standard-compliant parser.

Quirks mode and DOCTYPE switch locked old web with old rendering engine, IE5 engine.

I’m not wrong – after IE4 won first browsers war (BW I) nearly all sites were built for IE4 and IE5 only, so all other browsers had nothing to do but copy IE5 behaviour.

It’s important to note that DOCTYPE can only toggle standards compliancy on and off.

Therefore DOCTYPE switch only solves backwards compatibility issue and does nothing to prevent future compatibility issues.

When developers started using DOCTYPE switch, IE6 was a major browser and all the sites were build around its CSS support level. So when IE7 was shipped, it had better support for CSS2.1 but had the same DOCTYPE rendering modes switch.

That’s why many of these websites had rendering issues and had to fix CSS/JS for IE7 specifically. Conditional comments and conditional compilation came to rescue. However, with XP SP3 update IE6 started showing @jscript_version equal to that of IE7 and conditional compilation became pretty useless.

In IE7 old web-sites in quirks mode continued being rendered by IE5 rendering engine, and standards compliancy engine was just an updated version of IE6 standards compliancy rendering engine with some bugs fixed. And while IE7 was a minor update to IE6 but still caused so much trouble, one could imagine what IE8 with proper CSS2.1 support would do to websites. Basically, all the websites with DOCTYPES that were just fixed for IE7 Standards Compliancy mode would break in IE8 again.

Other browsers didn’t have a similar problem, and there’re many reasons to this. First of all, by the time when first IE7 beta was shipped, other browsers had really insignificant market share (so their developers could change anything and wouldn’t be blamed for “breaking the web”), were updated really often and were mostly installed by IT-related people who knew how to update the browser. So DOCTYPE rendering switch was and still is fine for non-IE browsers.

And Microsoft had to solve interesting problem in IE8 – support CSS2.1, support IE7 standards compliancy mode and support IE5 quirks mode. But DOCTYPE can only toggle standards compliancy on or off! They couldn’t drop support for very old but still existing sites build in quirks mode, couldn’t drop IE6 and IE7 support, and couldn’t leave IE8 without proper CSS2.1 support.

So we see that if any browser is locked at some version and this version gains noticeable market share, DOCTYPE switching will not work as vendors will have to choose what to support – older sites or newer features.

And Microsoft found a way – they gave web-developers and users a way to control in which mode sites are rendered.

IE8 Rendering Modes Theory

In IE8 there’re 6 rendering modes:

  • “Edge” Standard compliancy mode
  • IE8 Standards mode
  • “Emulate IE8” mode
  • IE7 Standards mode
  • “Emulate IE7” mode
  • IE5 – Quirks mode

Edge Standard compliancy mode basically tells the browser that it should use the most recent rendering mode – in IE8 it’s “IE8 Standards mode”, in IE9 it will hopefully be “IE9 Standards mode”.

IE8 Standards mode makes browser render a page using IE8 standards compliancy parser and renderer.

Emulate IE8 – DOCTYPE switch will be used to determine whether to use IE8 Standards compliancy mode or IE5 Mode.

IE7 Standards mode

makes browser render a page using IE7 Standards compliancy parser and renderer.

Emulate IE7 – DOCTYPE switch will be used to determine whether to use IE7 Standards compliancy mode or IE5 Mode. If you have a site that’s been built with IE7 Standards compliant mode support and you don’t want to change anything there, just add this META tag or HTTP header and IE8 (and all future versions of IE) will emulate IE7 behaviour, i.e. use IE7 Standards Compliant renderer when DOCTYPE is present and use IE5 mode when there’s no DOCTYPE. So you virtually “lock” your website to a current browser version behaviour. “Emulate IE7 mode” even sets User Agent string to IE7 string and report Version Vector as IE7 (so conditional comments will be evaluated as for IE7).

IE5 Mode – basically, it’s Quirks Mode. If we compare this to DOCTYPE switch approach, Edge mode is similar to a mode when DOCTYPE is set, and IE5 mode – to a mode when DOCTYPE is not present.

To explain modes in more details, I need to show how they are applied.

IE8 Rendering Modes Practice

As I’ve shown above, the whole idea of using DOCTYPE as a rendering mode switch failed because there were more than two types of documents on the web – those that should work in quirks mode, those that should work in IE7 standards mode, those that should work in IE8 standards compliancy mode and those that should work even with future versions of IE. So instead of two types we have three and a half :)

To give a proper way to control this, Microsoft and WaSP invented X-UA-Compatible http header, so you can either configure your server to send this header with corresponding value or add it as META HTTP-EQUIV to your page. For example:

<meta http-equiv="X-UA-Compatible" content="IE=8">

This declaration will tell IE8 to toggle IE8 Standards compliancy mode to render the page.

Here’s a list of modes:

Rendering mode X-UA-Compatible value
Edge Standard compliancy IE=edge
IE8 Standards compliancy IE=8
IE8 Emulation IE=EmulateIE8
IE7 Standards compliancy IE=7
IE7 Emulation IE=EmulateIE7
IE5 Quirks IE=5

 

IE8 Rendering Modes Switches Priority

First and main rule – if you specify X-UA-Compatible header or meta tag, IE8 will use the rendering engine you set.

If it’s EmulateIE7, DOCTYPE switch will be used to determine whether to use IE7 Standards mode or IE5 Quirks mode. See test case with DTD and without DTD.

If it’s EmulateIE8, DOCTYPE switch will be used to determine if it should use IE8 Standards mode or IE5 Quirks mode. See test case with DTD and without DTD.

If it’s IE=8, IE8 Standards Compliancy mode will be used regardless of presence or absence of DOCTYPE. See test case with DTD and without DTD.

If it’s IE=7, IE7 Standards Compliancy mode will be used regardless of presence or absence of DOCTYPE. See test case with DTD and without DTD.

If it’s IE=5, IE5 Quirks mode is used even if there is a DOCTYPE (actually it didn’t matter for IE5 if there was DOCTYPE or not – quirks mode was the only mode it had). See test case.

If it’s Edge, most modern rendering engine is used. For example, IE8 uses IE=8 mode regardless if there’s a DOCTYPE or not and IE9 will use IE=9. See test case with DTD and without DTD.

It’s worthy to note that X-UA-Compatible META overwrites X-UA-Compatible HTTP header, so you can set X-UA-Compatible HTTP header for the whole website and then have X-UA-Compatible META with a different value on some pages.

If website doesn’t have X-UA-Compatible META tag or HTTP header, IE8 uses EmulateIE8 mode i.e. DOCTYPE is used to switch between IE8 standards compliancy mode and IE5 Quirks mode.

This means that everybody needs to add to add X-UA-Compatible header or META to their websites.

Accepting the fact that web-developers wouldn’t update all the websites that were built around IE6/IE7 standards compliancy mode, Microsoft decision to take standards compliancy mode as a default would certainly “break the web”.

So Microsoft had to find a way around this which they successfully did by introducing a bunch of Compatibility View settings.

Compatibility View

As I’ve already said, when developer didn’t care about adding X-UA-Compatible header or META, IE8 works in EmulateIE8 mode and uses DOCTYPE switch to determine which rendering mode to use. If the site hasn’t got a DOCTYPE set, IE5 Quirks mode will be used, so backwards compatibility is not an issue. But if there’s a DOCTYPE, which Standards compliant mode to use? IE7? IE8? IE9 Standards Compliancy mode when IE9 it’s shipped? There’s no automatic way to choose.

And as IE8 Standards mode is a default one, there might be issues on sites that only supported IE6/IE7 Standard modes. Apparently, only user can see if the site he’s viewing has rendering issues. And Microsoft gave users an easy way to switch back to IE7 Standards Compliancy mode if required by clicking on a Compatibility View button (located to the left of refresh button).

image

IE8 will switch to EmulateIE7 mode and save the setting for the domain so all the pages from this domain will be rendered using IE7 engine.

Compatibility View button is shown only when X-UA-Compatible HTTP header or META is not set.

Also during IE8 install a user can specify if he wants to use Compatibility View List feature. Basically, when this feature is turned and user clicks Compatibility View button while browsing, IE8 reports to Microsoft that the website on this domain required user to press the Compatibility View button.

If statistics show that users tend to press Compatibility View button for the site, it’s included in iecompatdata.xml file which fresh copy is distributed with Windows Update. It can be viewed by typing res://iecompat.dll/iecompatdata.xml into the address bar.

So when Compatibility View List feature is enabled and there’s no X-UA-Compatible HTTP header or META, and DOCTYPE is present, IE8 will look if the site is in the res://iecompat.dll/iecompatdata.xml file and make Compatibility View button pressed by default.

As soon as the site gets X-UA-Compatible header or META tag, IE8 will use the rendering mode from its value and won’t even check Compatibility View List and also Compatibility View button won’t show at all.

Local Intranet security zone

Intranet differs from Internet a lot.

If Compatibility View List feature will work fine for public internet websites, Intranet sites should not be reported anywhere. So what rendering engine to use when there’s no X-UA-Compatible value and DOCTYPE is present? IE7 Standards Compliancy? IE8 Standards Compliancy?

Intranet a controlled environment where to decrease support costs IT pros usually set everybody with same set of software. As most successful companies use Microsoft technologies as a core of their IT infrastructure and because only IE has been providing a decent platform for building powerful intranet web applications, IE was always a de-facto standard browser in companies.

Many intranet web applications were targeting IE5.5/6.0 and nobody cared to refactor to get standards compliancy – why bother if there was always only one browser supporting a well-known list of technologies so cross-browser interoperability was never an issue? We can argue if this approach is good or not – but IE8 team had thousands of intranet applications they had to support. That’s why compatibility view was the only option for sites from Local Intranet security zone.

If the site is in the Local Intranet zone and there’s no X-UA-Compatible header or meta tag, DOCTYPE switch is used to determine which rendering engine to take – IE7 Standards compliancy or IE5 quirks mode.

If there’s no X-UA-Compatible value and site is in Local Intranet security zone, it will be rendered in EmulateIE7 mode by default.

Compatibility View button is not displayed at all.

But as soon as a developer adds X-UA-Compatible header or META, site will start working in the specified mode.

Developer Tools

With great set of developer tools that Microsoft provided for IE8 you can also view what rendering is being used and change it if required. This is useful for developers only and overrides any other settings. It’s very useful for testing – you don’t need IE7 in a virtual machine to test how a page looks in IE7 – just press F12 to get the developer tools and play with Browser modes or Document modes :)

Hope I managed to cover all the modes. In future posts I’ll write more on this topic.

P.S. As SelenIT pointed in comments, it’s very important that X-UA-Compatible meta tag must be put before any other elements in the head section, otherwise it might not work!

Share:

Embedded fonts

Posted in browsers, css by sharovatov on 27 April 2009

Long long ago (before IE4, yes, IE4) Microsoft  proposed a standard called EOT (Embedded OpenType) which allowed you to embed any font on your website – all you had to do was to prepare eot fonts in a free WEFT tool (see nice how-to) and then reference them in your CSS:

@font-face {
    font-family: myFont;
    src: url(myfont.eot);
}

h1 { font-family: myFont; }

It’s interesting to know that support for @font-face property appeared in CSS2.0 without specifying of font format, then was suddenly dropped in CSS2.1 and now is back in CSS3.

And now, 10 years later after this feature has been introduced in IE4, all other browsers are slowly starting to implement embedded fonts support. As always, browser vendors talk about compatibility more than actually support this compatibility – while the technology is 10 years old and quite mature, none of popular browsers supports or plans to support EOT – only IE.

And this silent boycott of EOT looks extremely weird because EOT has got a unique feature – font file in this format can be much smaller than a TTF/OTF file due to subsetting. And EOT is not proprietary any more – Microsoft has submitted it to W3C. The only reason browser vendors say stops them from implementing EOT is DRM, but:

  1. as Mark Wubben says, using OTF/TTF can be violating fonts EULA while EOT was designed to follow the rules.
  2. there’re free fonts that can be embedded.

And it’s really funny to read rants like this – if there’s a law, you can’t just violate it because you think it’s too hard to follow it.

And while browser vendors pretend they don’t see the industry standard implementation of the technology, we’ll have to use something like this:

@font-face {
   font-family: myFont;
   src: url(font.eot);
}

@font-face {
   font-family: myFont;
   src: url(font.ttf) format("truetype"),
      url(font.eot) format("embedded-opentype");
}

I.e. set the @font-face twice – for IE and other browsers. More crap for us, developers. Thanks to Opera, Mozilla and Safari.

Update: Thanks to Philip Taylor, author of great web fonts application, he pointed in comments that I was wrong saying that TTF/OTF didn’t support subsetting – they did! But my point is still the same – why inventing other standards when there’s a working one?

Links:

Tagged with: , ,

don’t use @import

Posted in browsers, css by sharovatov on 15 April 2009

Steve Souders recently posted a very interesting blogpost about the little-known negative impact on page perfomance that @import rule causes – it doesn’t let IE to download stylesheets in parallel.

Please proceed to his post for more details.

Thanks for sharing this, Steve!

Share:

Weird bug with hiding columns in IE7

Posted in css, IE7, IE8, javascript by sharovatov on 14 April 2009

Sometimes when you create a table, you need to dynamically toggle certain columns visibility. I usually use the following approach:

  1. Set those columns that are supposed to be hidden and shown with a className, e.g. tohide
  2. Set their parent with some className, say, hiddenChildren.
  3. Define the following CSS rule: .hiddenChildren .tohide { display: none }, which basically says that if there is a parent node with className="hiddenChildren" and it has child nodes with className="tohide", all these child nodes will be hidden.
  4. When I need to show cells, I remove this hiddenChildren class from parent’s className, and when I need to hide them again, I add it back.

This is a very common scenario and it’s much more efficient than the approach where you go through all the elements in javascript and toggle their personal display mode.

There’s also an inverted approach where by default elements are hidden and assigned class specifies positive display – table-cell for CSS2.1-compliant browsers and block for IE6. But my approach is better as it’s cleaner – there’s no need for IE6-specific code (usually assigned by star hack).

However, in IE7 with both of the approaches the cells’ display is not switched as you may notice by viewing the testcase.

But as in most cases, there’s a workaround – you have to “poke” any TH of your table. In this testcase I added the following code:

/*@cc_on if (!document.querySelector)
    document.getElementsByTagName('th')[0].className = 'wt2'; @*/

to the toggling function. As you may know, js code within /*@cc_on … @*/ block will be executed only by IE, and only IE8 has support for document.querySelector interface, so chances are that this code will be executed only in IE with version earlier than 8, which is precisely what we need (as IE8 doesn’t have this problem).

It’s interesting to know, that YUI Team in their DataTable control in order to implement hiding/showing functionality choosed to loop through all the cells and set their display individually (see hideColumn/showColumn functions here). I understand that this is the bulletproof approach ready to use in any environment, but it can be rather ineffective when your table has a significant amount of rows as it will have to go through all the TR’s. And it will do the loop twice if you choose to hide two columns :) Of course, using my approach requires setting classes for those cells that you were going to hide, so it’s not that flexible, but in the controlled environment it’s definitely the best option.

IE8b1 — expressions support

Posted in browsers, css, javascript by sharovatov on 16 May 2008

Internet Explorer keeps on changing. One example is expressions — their support is dropping from version to version. In different versions of IE this testcase produces the following output.

So IE started filtering values in expressions since IE6, and now IE8b1 in both modes doesn't even allow you to use object property accessors (both dot and square brackets notations — see testcase). So in IE8b1 in expressions you can only use plain string values in your expressions (which is not handy at all) or call externally defined functions.

I can't help thinking of any other reason for disabling this except for protecting from potential XSS threat that I described in the previous post.

Also in IE8b1 expressions are not reevaluated on mouseover event (see testcase), but onscroll still fires document.recalc (this again seems to be left intentionally in order to support all cludges that were invented to implement for example non-existent position: fixed CSS rule).

Bottom line, if you have expressions used in your CSS code, don't wait — separate all the stuff you do there to JS functions and just call these functions from your expressions.

Tagged with: ,

data URI browser issues

Posted in browsers, css, javascript by sharovatov on 13 May 2008

Length limit

Theory

data URI specification says the following:

some applications that use URLs may impose a length limit; for
example, URLs embedded within <A> anchors in HTML have a length limit
determined by the SGML declaration for HTML [RFC1866]. The LITLEN
(1024) limits the number of characters which can appear in a single
attribute value literal, the ATTSPLEN (2100) limits the sum of all
lengths of all attribute value specifications which appear in a tag,
and the TAGLEN (2100) limits the overall length of a tag.

Though at the time of writing data URI specification HTML3.2 was current HTML recommendation, author intentionally used LITLEN, ATTSPLEN and TAGLEN values from the older HTML2.0 SGML declaration to show that some user-agents may impose a length limit for URI.

HTTP1.1 doesn’t put a limit on the length of URI, but it warns us:

Note: Servers ought to be cautious about depending on URI lengths
above 255 bytes, because some older client or proxy
implementations might not properly support these lengths.

which basically means that if all clients in the network support URIes more than 255 bytes long, we’re ok.

HTML3.2 SGML declaration states the maximum length of an attribute to be 65535. Even more, HTML4.01 SGML declaration uses value 65535 as a maximum allowed in SGML but says that fixed limits should be avoided. XML1.0 SGML declaration uses 99999999 value just to show that there’s no limit specified.

Practice

Different browsers have different maximum length of dataURI’ed values supported.

As per the kb208427 article, IE supports URI length of up to 2048 bytes. According to the Microsoft IE8 data URI support whitepaper, IE8 supports up to 32Kbytes of data URI and silently discards dataURI value if its size exceeds 32Kbytes (which can be checked in the testcase1 and testcase2). As I’ve already mentioned in the previous post, other browsers provide bigger-sized URI support, but I doubt that IE8 will have minor market share so we will still have to stick to 32Kbytes. And I will repeat: data URI spec author that the only reasonable and semantic use of data URI is embedding small resources, so realistically speaking 32Kbytes limit shouldn’t be a problem.

Serving CSS dataURI’ed

In theory, CSS has to be served with its MIME type (text/css).

In practice, only Firefox and only in standards compliancy mode cares about MIME type that CSS’s been served with. Please see the testcases with CSS served with wrong MIME type in different render modes: in standards compliancy mode and in quirks mode. Opera, Safari and Internet Explorer 8 all apply CSS served with any MIME type in all modes. The behaviour is the same for both CSS files served using dataURI and by referencing normal external files.

Serving Javascript and HTML dataURI’ed

Safari, Opera and Firefox support embedding javascript using data URI scheme. According to the whitepaper, IE8b1 doesn’t support this. Here’s the quote:


Scripts in data URIs are unsupported because they allow potentially harmful script to bypass server- and

proxy-script filters for applications such as HTML email. (Web-based email clients generally do not allow

emails to execute script; data URIs could be used to easily bypass these filters).

I do agree that this is a valid point, it is a potential security issue, dataURIed javascript is even published as an XSS vector. Please see the testcase.

Opera and Safari run dataURI’ed HTML page in a separate isolated context, IE8b1 doesn’t support dataURI’ed html at all, so the only affected browser here is Firefox. There’s an interesting bugzilla entry describing the XSS (marked as duplicate to the security proposal) which says:


The attack works by exploiting an ambiguity in RFC 2397 with regard to the Javascript same-origin security policy — what is the origin of a URI? Is it the containing page? If so, preventing this attack is the responsibility of site maintainers. If not, FF should launch the child of a data: URI without

same-origin privileges.

Firefox authors reply that this is site maintainers’ problem to filter dataURI and compare this to filtering javascript: (which is quite fair) but why did they want to create a new hole in all the sites for some vague benefits of executing dataURI’ed scripts in the same context?

To me it seems a bit weird especially looking at the fact that other browsers do care about execution context. The bugzilla entry is still opened, but I doubt that this is going to be fixed. So, “site maintainers”, be aware!

Nested dataURI’es

Neither dataURI spec nor any other mentions if dataURI’es can not be nested. So here’s the testcase where dataURI’ed CSS has dataURI’ed image embedded. IE8b1, Firefox3 and Safari applied the stylesheet and showed the image, Opera9.50 (build 9613) applies the stylesheet but doesn’t show the embedded image! So it seems that Opera9 doesn’t expect to get anything embedded inside of an already embedded resource! :D

But funny thing, as IE8b1 supports expressions and also supports nested data URI’es, it has the same potential security flaw as Firefox does (as described in the section above). See the testcase — embedded CSS has the following code: body { background: expression(a()); } which calls function a() defined in the javascript of the main page, and this function is called every time the expression is reevaluated. Though IE8b1 has limited expressions support (which is going to be explained in a separate post) you can’t use any code as the expression value, but you can only call already defined functions or use direct string values. So in order to exploit this feature we need to have a ready javascript function already located on the page and then we can just call it from the expression embedded in the stylesheet. That’s not very trivial obviously, but if you have a website that allows people to specify their own stylesheets and you want to be on the safe side, you have to either make sure you don’t have a javascript function that can cause any potential harm or filter expressions from people’s stylesheets.

Line feeds

Firefox, Opera, Safari and IE8b1 support both data URI values supplied as one line (as an URI) and splitted by 76 bytes (as specified in MIME and MHTML RFCs). See the testcase.

But base64 RFC doesn’t put a requirement to split base64 strings:

MIME [3] is often used as a reference for base 64 encoding.  However,
MIME does not define "base 64" per se, but rather a "base 64
Content-Transfer-Encoding" for use within MIME.  As such, MIME
enforces a limit on line length of base 64 encoded data to 76
characters.  MIME inherits the encoding from PEM [2] stating it is
"virtually identical", however PEM uses a line length of 64
characters.  The MIME and PEM limits are both due to limits within
SMTP.

Implementations MUST NOT not add line feeds to base encoded data
unless the specification referring to this document explicitly
directs base encoders to add line feeds after a specific number of
characters.

DataURIed images with images turned off

When you turn off images in your browser, only Firefox still shows dataURIed images. IE8b1, Safari and Opera don’t show the image as it’s supposed to be when you turn the images off. To test this turn off images in your browser and run the testcase.

UPDATE: Firefox developers told me this is by design as unchecking “Load images automatically” option in the browser settings disables only network request to get the image. So if the image is accessible without doing a network request — either from cache or embedded as dataURI, it will be displayed in any case.

Dynamically created dataURIes

As dataURI can contain binary data (e.g. to show images), there are thoughts on using this. Ajaxian has a crazy article on creating pure JS video player that doesn’t use flash but changes dataURI’ed images instead. This technique may get some practical evolution and usage, but now it’s rather impractical.

Links and references

P.S. And here’s a great tool from Nicholas C. Zakas – CSSEmbed – it reads the CSS file you want, finds all the images references, converts images to dataURIes and saves resulting CSS file. So if you have small presentational images referenced in your CSS file, feed CSSEmbed with your CSS file and you’ll get them all converted in one go! Nice and simple! This tool can be also easily integrated into your publishing process if required. Thanks Nicholas!

Share :

Tagged with: , , , ,

data URI theory and practice

Posted in browsers, css by sharovatov on 11 May 2008

Theory

Data URI'es is an RFC 2397 published in 1998. It's a URL scheme which is used to embed small resources right into the (X)HTML page.

Syntax is quite simple: data:[<mediatype>][;base64],<data>

To see how it works let's take the following code (testcase):

<link rel="stylesheet" type="text/css" href="data:text/css;base64,Ym9keXtiYWNrZ3JvdW5kOmdyZWVuO30=">

Browser supporting data URI will base64-decode the encoded string Ym9keXtiYWNrZ3JvdW5kOmdyZWVuO30= to body{background:green;} and then load this string as if it was a result of an http request to an external file containing this CSS code.

According to the RFC we can embed any small resource into our page, e.g:

  1. images (as img elements and CSS backgrounds)
  2. javascript
  3. html (links, iframes)
  4. css (and even dataURIed images inside dataURIed CSS!)
  5. any other resource supported by browsers

So theoretically we could have the same functionality as we have in MHTML — some or all external resources embedded directly in the page.

All data URI advocates say that as most of the browsers have 2 concurrent connections per server (but 6 in total), dataURI mechanism potentially can speed up page load by decreasing the amount of HTTP requests (especially in case of HTTPS where encrypting payload produces quite big overhead). But:

  • HTTP protocol already has methods to help building efficient applications — persistent connections to avoid recreating the sockets, different caching mechanisms to reduce overhead (Conditional GET) or avoid total amount of requests (aggressive caching using Expires header).
  • Even more, using simple technique you can have your browser use 6 concurrent connections to parallelize fetching data as much as it can and therefore fasten page load.
  • Though HTTP 1.1 spec says that we shouldn't have more than 2 concurrent connections per server, in real world we have 2 concurrent connections only in Firefox and IE6/7. In IE8b1 the number is 6, in Opera 9 and Safari it's 4. In the next post I will give more details on this.

So keeping all this in mind we can't just say that dataURI is the only usable way to improve page load times. But it definetely is the only option when you have a limited access to the server and/or the server is not configured properly, so you can't set Expires header for aggressive caching, you can't set DNS wildcards or CNAME records to get your resources served from different hosts (and therefore leverage the maximum available concurrent connection in browsers) or server doesn't support HTTP caching properly.

Practice

So I can see only the following cases where dataURI can be effectively used:

  • CSS sprites, rounded corners images, icons and other images that have only presentational semantics. It's the perfect target for dataURI + base64 to be applied to. If we embed them in the CSS file, we remove HTTP requests that would be queried if these images were normal files. These images are part of the design described in the stylesheet, so it makes perfect sense to embed them in CSS. CSS files can be perfectly cached and while design doesn't change, we don't need to touch this CSS and change anything. But there should be a common sense here as well — firstly, base64 decoding takes system resources and secondly, who wants to wait for a CSS file of couple hundred kilobytes in size to load?
  • Reasonably small CSS files with rules specific for a page. If there is a semantical sense to define an inline CSS on a page, then there's a perfect sense to set it using dataURI. Another thing is that if CSS file is not going to be parsed until it’s fully downloaded by a browser. So when we embed a big image, we’ll have first client opening the page wait till CSS is fully loaded. So we loose our HTTP parallelism benefits here.

Please don't forget that if a resource is embedded on multiple pages, it's obviously going to be redownloaded as many times as these containing pages are. And if a resource is not dataURI'ed but referenced normally as an external file, it can be cached quite aggressively and requested from the server only once (all popular web-servers already provide good caching support for static files).

However, this is all ideal world where specification don't have flaws and all the browsers follow them.

In our world we have the following:

  • Lack of support. Only IE8b1/Opera9/Firefox2/Safari support data URI. No IE6/IE71. That means that for the next three or four years while IE6 and IE7 will still have a significant market share, we can't just go and start using dataURI.
  • Different size limits on URI length in different browsers. As far as I know for now IE8 supports up to 32 kilobytes in data: value. Even though all other browsers support bigger sizes, our limit will obviously be 32Kb.
    See testcase 1 with data URI of 32755 bytes and testcase2 with dataURI of 32868 bytes.

Also I would strongly discourage from dynamically base64-encoding and embedding images in CSS files by some scripting language unless you're well aware of HTTP caching principles.

Let's consider the following composed code from Wikipedia data:URI page:

<?php
function data_url($file, $mime) 
{  
  $contents = file_get_contents($file);
  $base64   = base64_encode($contents); 
  return ('data:' . $mime . ';base64,' . $base64);
}

header('Content-type: text/css');

?>
 
div.menu {
  background-image:url(<?php echo data_url('menu_background.png','image/png')?>);
}

Unless accompanied with correct HTTP caching algorythm, this CSS file will be downloaded every time the page that references this CSS file is loaded! So every time user accesses the page referencing this CSS file, server will get a request, initiate script parsing, base64-encode the image and send it back to client. So you get rid of one simple request for an image (that in case of being a static file will be perfectly cached) but have one heavy request that will be run every time user requests a page! Not a fair change I think. So again, if you decide to use data URI scheme for your resources, encode and embed them beforehand or implement proper server-side HTTP caching and compressing support.

Note for russian-speaking users: — there's a way to embed images even for IE6/IE7. Though it's rather a proof-of-concept — it doesn't support HTTP caching/compressing, but it works!

Links and resources:

IE8b1 — attribute selectors, generated content

Posted in browsers, css, javascript by sharovatov on 6 May 2008

It's my first post after a great vacation in St.Petersburg — my first 2 weeks vacation in last 4 years. I've continued testing IE8 and found some new interesting stuff.

Attribute selectors

Both [class=myclass] and [className=myclass] work in IE7/IE8. The last one can be used as a CSS hack to target those browsers, but I would still recommend using conditional comments to target different IE versions.

If you look at the testcase, you will see that both [class=test1] and [classname=test2] selectors work. When I saw className working, I immediately tested other DOM properties like nodeName. Unfortunately, it didn't work there — here's the testcase. If it did, if there was such a way to access not HTML attribute but DOM properties from CSS selectors, it would be really weird but interesting.

Generated content

When I was testing it, I noticed that if you want to get element's class, you can't use content: attr(class) rule, you have to use content: attr(className). It's obvious that this is a DOM property name rather than HTML element's attribute.

This violates the standard which clearly says that attr(X) must return an attribute string value for the element matching the selector. It also violates the standard by returning null value for not existing attributes.

This behavior also gives us some strange options. Please see the testcase.

I don't know if it's a bug or a feature — none of the Microsoft documents on IE8 describes this behaviour, so I don't know if this is going to be fixed or not; but it may be used in some interesting ways.
E.g. using outerHTML IE-only DOM property I rebuilt the testcase for the attribute selectors bug mentioned above. If you have IE8, don't wait to have a look. And please have a look at another interesting thing — again it's IE8-only as it uses attr(nodeName) function to show every element's nodeName.

During testing I've noticed some more bugs with generated content:

  • text-transform doesn't work for generated content. Please see testcase
  • text-indent doesn't work for generated content. Here's the testcase
  • text-align doesn't work for generated content. The testcase