5e speculative background images

February 12, 2010 6:09 pm | 13 Comments

This is the fifth of five quick posts about some browser quirks that have come up in the last few weeks.

Chrome and Safari start downloading background images before all styles are available. If a background image style gets overwritten this may cause wasteful downloads.

Background images are used everywhere: buttons, background wallpaper, rounded corners, etc. You specify a background image in CSS like so:

.bgimage { background-image: url("/images/button1.gif"); }

Downloading resources is an area for optimizing performance, so it’s important to understand what causes CSS background images to get downloaded. See if you can answer the following questions about button1.gif:

  1. Suppose no elements in the page use the class “bgimage”. Is button1.gif downloaded?
  2. Suppose an element in the page has the class “bgimage” but also has “display: none” or “visibility: hidden”. Is button1.gif downloaded?
  3. Suppose later in the page a stylesheet gets downloaded and redefines the “bgimage” class like this:
    .bgimage { background-image: url("/images/button2.gif"); }

    Is button1.gif downloaded?

Ready?

The answer to question #1 is “no”. If no elements in the page use the rule, then the background image is not downloaded. This is true in all browsers that I’ve tested.

The answer to question #2 is “depends on the browser”. This might be surprising. Firefox 3.6 and Opera 10.10 do not download button1.gif, but the background image is downloaded in IE 8, Safari 4, and Chrome 4. I don’t have an explanation for this, but I do have a test page: hidden background images. If you have elements with background images that are hidden initially, you should hold off on creating them until after the visible content in the page is rendered.

The answer to question #3 is “depends on the browser”. I find this to be the most interesting behavior to investigate. According to the cascading behavior of CSS, the latter definition of the “bgimage” class should cause the background-image style to use button2.gif. And in all the major browsers this is exactly what happens. But Safari 4 and Chrome 4 are a little more aggressive about fetching background images. They download button1.gif on the speculation that the background-image property won’t be overwritten, and then later download button2.gif when it is overwritten. Here’s the test page: speculative background images.

When my officemate, Steve Lamm, pointed out this behavior to me, my first reaction was “that’s wasteful!” I love prefetching, but I’m not a big fan of most prefetching implementations because they’re too aggressive – they err too far on the side of downloading resources that never get used. After my initial reaction, I thought about this some more. How frequently would this speculative background image downloading be wasteful? I went on a search and couldn’t find any popular web site that overwrote the background-image style. Not one. I’m not saying pages like this don’t exist, I’m just saying it’s very atypical.

On the other hand, this speculative downloading of background images can really help performance and the user’s perception of page speed. Many web sites have multiple stylesheets. If background images don’t start downloading until all stylesheets are done loading, the page takes longer to render. Safari and Chrome’s behavior of downloading a background image as soon as an element needs it, even if one or more stylesheets are still downloading, is a nice performance optimization.

That’s a nice way to finish the week. Next week: my Browser Performance Wishlist.

The five posts in this series are:

13 Comments

5d dynamic stylesheets

February 12, 2010 1:13 am | 7 Comments

This is the fourth of five quick posts about some browser quirks that have come up in the last few weeks.

You can avoid blocking rendering in IE if you load stylesheets using DHTML and setTimeout.

A few weeks ago I had a meeting with a company that makes a popular widget. One technique they used to reduce their widget’s impact on the main page was to load a stylesheet dynamically, something like this:

var link = document.createElement('link');
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = '/main.css';
document.getElementsByTagName('head')[0].appendChild(link);

Most of my attention for the past year has been on loading scripts dynamically to avoid blocking downloads. I haven’t focused on loading stylesheets dynamically. When it comes to stylesheets, blocking downloads isn’t an issue – stylesheets don’t block downloads (except in Firefox 2.0). The thing to worry about when downloading stylesheets is that IE blocks rendering until all stylesheets are downloaded1, and other browsers might experience a Flash Of Unstyled Content (FOUC).

FOUC isn’t a concern for this widget – the rules in the dynamically-loaded stylesheet only apply to the widget, and the widget hasn’t been created yet so nothing can flash. If the point of loading the stylesheet dynamically is to not mess with the containing page, we have to make sure dynamic stylesheets don’t block the page from rendering in IE.

I created the DHTML stylesheet example to show what happens. The page loads a stylesheet dynamically. The stylesheet is configured to take 4 seconds to download. If you load the page in Internet Explorer the page is blank for 4 seconds. In order to decouple the stylesheet load from page rendering, the DHTML code has to be invoked using setTimeout. That’s what I do in the DHTML + setTimeout stylesheet test page. This works. The page renders immediately while the stylesheet is downloaded in the background.

This technique is applicable when you have stylesheets that you want to load in the page but the stylesheet’s rules don’t apply to any DOM elements in the page currently. This is a pretty small use case. It makes sense for widgets or pages that have DHTML features that aren’t invoked until after the page has loaded. If you find yourself in that situation, you can use this technique to avoid the blank white screen in IE.

The five posts in this series are:

1 Simple test pages may not reproduce this problem. My testing shows that you need a script (inline or external) above the stylesheet, or two or more stylesheets for rendering to be blocked. If your page has only one stylesheet and no SCRIPT tags, you might not experience this issue.

7 Comments

5c media=print stylesheets

February 11, 2010 5:27 pm | 2 Comments

This is the third of five quick posts about some browser quirks that have come up in the last few weeks.

Stylesheets set with media=”print” still block rendering in Internet Explorer.

A few weeks ago a friend at a top web company pinged me about a possible bug in Page Speed and YSlow. Both tools were complaining about stylesheets he placed at the bottom of his page, an obvious violation of my put stylesheets at the top rule from High Performance Web Sites. The reasoning behind this rule is that Internet Explorer won’t start rendering the page until all stylesheets are downloaded1, and other browsers might produce the Flash Of Unstyled Content (FOUC). It’s best to put stylesheets at the top so they get downloaded as soon as possible.

His reason for putting these stylesheets at the bottom was that they were specified with media="print". Since these stylesheets weren’t going to be used to render the current page, he wanted to load them last so that other more important resources could get downloaded sooner. Going back to the reasons for the “put stylesheets at the top” rule, he wouldn’t have to worry about FOUC (the stylesheets wouldn’t be applied to the current page). But would he have to worry about IE blocking the page from rendering? Time for a test page.

The media=print stylesheets test page contains one stylesheet at the bottom with media="print". This stylesheet is configured to take 4 seconds to download. If you view this page in Internet Explorer you’ll see that rendering is indeed blocked for 4 seconds (tested on IE 6, 7, & 8).

I’m surprised browsers haven’t gotten to the point where they skip downloading stylesheets for a different media type than the current one. I’ve asked some web devs but no one can think of a good reason for doing this. In the meantime, even if you have stylesheets with media="print" you might want to follow the advice of Page Speed and YSlow and put them in the document HEAD. Or you could try loading them dynamically. That’s the topic I’ll cover in my next blog post.

The five posts in this series are:

1 Simple test pages may not reproduce this problem. My testing shows that you need a script (inline or external) above the stylesheet, or two or more stylesheets for rendering to be blocked. If your page has only one stylesheet and no SCRIPT tags, you might not experience this issue.

2 Comments

5a Missing schema double download

February 10, 2010 5:12 pm | 15 Comments

This is the first of five quick posts about some browser quirks that have come up in the last few weeks.

Internet Explorer 7 & 8 will download stylesheets twice if the http(s) protocol is missing.

If you have an HTTPS page that loads resources with “http://” in the URL, IE halts the download and displays an error dialog. This is called mixed content and should be avoided. How should developers code their URLs to avoid this problem? You could do it on the backend in your HTML template language. But a practice that is getting wider adoption is protocol relative URLs.

A protocol relative URL doesn’t contain a protocol. For example,

https://stevesouders.com/images/book-84x110.jpg

becomes

//stevesouders.com/images/book-84x110.jpg

Browsers substitute the protocol of the page itself for the resource’s missing protocol. Problem solved! In fact, today’s HttpWatch Blog posted about this: Using Protocol Relative URLs to Switch between HTTP and HTTPS.

However, if you try this in Internet Explorer 7 and 8 you’ll see that stylesheets specified with a protocol relative URL are downloaded twice. Hard to believe, but true. My officemate, Steve Lamm, discovered this when looking at the new Nexus One Phone page. That page fetches a stylesheet like this:

<link type="text/css" rel="stylesheet" href="//www.google.com/phone/static/2496921881-SiteCss.css">

Notice there’s no protocol. If you load this page in Internet Explorer 7 and 8 the waterfall chart (nicely generated by HttpWatch) looks like this:

Notice 2496921881-SiteCss.css is downloaded twice, and each time it’s a 200 response, so it’s not being read from cache.

It turns out this only happens with stylesheets. The Missing schema, double download test page I created contains a stylesheet, an image, and a script that all have protocol relative URLs pointing to 1.cuzillion.com. The stylesheet is downloaded twice, but the image and script are only downloaded once. I added another stylesheet from 2.cuzillion.com that has a full URL (i.e., it starts with “http:”). This stylesheet is only downloaded once.

Developers should avoid using protocol relative URLs for stylesheets if they want their pages to be as fast as possible in Internet Explorer 7 & 8.

The five posts in this series are:

15 Comments

Browser script loading roundup

February 7, 2010 12:12 am | 8 Comments

How are browsers doing when it comes to parallel script loading?

Back in the days of IE7 and Firefox 2.0, no browser loaded scripts in parallel with other resources. Instead, these older browsers would block all subsequent resource requests until the script was received, parsed, and executed. Here’s how the HTTP requests look when this blocking occurs in older browsers:

The test page that generated this waterfall chart has six HTTP requests:

  1. the HTML document
  2. the 1st script – 2 seconds to download, 2 seconds to execute
  3. the 2nd script – 2 seconds to download, 2 seconds to execute
  4. an image – 1 second to download
  5. a stylesheet- 1 second to download
  6. an iframe – 1 second to download

The figure above shows how the scripts block each other and block the image, stylesheet, and iframe, as well. The image, stylesheet, and iframe download in parallel with each other, but not until the scripts are finished downloading sequentially.

The likely reason scripts were downloaded sequentially in older browsers was to preserve execution order. This is critical when code in the 2nd script depends on symbols defined in the 1st script. Preserving execution order avoids undefined symbol errors. But the missed opportunity is obvious – while the browser is downloading the first script and guaranteeing to execute it first, it could be downloading the other four resources in parallel.

Thankfully, newer browsers now load scripts in parallel!

This is a big win for today’s web apps that often contain 100K+ of JavaScript split across multiple files. Loading the same test page in IE8, Firefox 3.6, Chrome 4, and Safari 4 produces an HTTP waterfall chart like this:

Things look a lot better, but not as good as they should be. In this case, IE8 loads the two scripts and stylesheet in parallel, but the image and iframe are blocked. All of the newer browsers have similar limitations with regard to the extent to which they load scripts in parallel with other types of resources. This table from Browserscope shows where we are and the progress made to get to this point. The recently added “Compare” button added to Browserscope made it easy to generate this historical view.

While downloading scripts, IE8 still blocks on images and iframes. Chrome 4, Firefox 3.6, and Safari 4 block on iframes. Opera 10.10 blocks on all resource types. I’m confident parallel script loading will continue to improve based on the great progress made in the last batch of browsers. Let’s keep our eyes on the next browsers to see if things improve even more.

8 Comments

Speed Tracer – visibility into the browser

December 10, 2009 7:46 am | 9 Comments

Is it just me, or does anyone else think Google’s on fire lately, lighting up the world of web performance? Quick review of news from the past two weeks:

Speed Tracer was my highlight from last night’s Google Campfire One. The event celebrated the release of GWT 2.0. Performance and “faster” were emphasized again and again throughout the evening’s presentations (I love that). GWT’s new code splitting capabilities are great for performance, but Speed Tracer easily wowed the audience – including me. In this post, I’ll describe what I like about Speed Tracer, what I hope to see added next, and then I’ll step back and talk about the state of performance profilers.

Getting started with Speed Tracer

Some quick notes about Speed Tracer:

  • It’s a Chrome extension, so it only runs in Chrome. (Chrome extensions is yet another announcement this week.)
  • It’s written in GWT 2.0.
  • It works on all web sites, even sites that don’t use GWT.

The Speed Tracer getting started page provides the details for installation. You have to be on the Chrome dev channel. Installing Speed Tracer adds a green stopwatch to the toolbar. Clicking on the icon starts Speed Tracer in a separate Chrome window. As you surf sites in the original window, the performance information is shown in the Speed Tracer window.

Beautiful visibility

When it comes to optimizing performance, developers have long been working in the dark. Without the ability to measure JavaScript execution, page layout, reflows, and HTML parsing, it’s not possible to optimize the pain points of today’s web apps. Speed Tracer gives developers visibility into these parts of page loading via the Sluggishness view, as shown here. (Click on the figure to see a full screen view.) Not only is this kind of visibility great, but the display is just, well, beautiful. Good UI and dev tools don’t often intersect, but when they do it makes development that much easier and more enjoyable.

Speed Tracer also has a Network view, with the requisite waterfall chart of HTTP requests. Performance hints are built into the tool flagging issues such as bad cache headers, exceedingly long responses, Mozilla cache hash collision, too many reflows, and uncompressed responses. Speed Tracer also supports saving and reloading the profiled information. This is extremely useful when working on bugs or analyzing performance with other team members.

Feature requests

I’m definitely going to be using Speed Tracer. For a first version, it’s extremely feature rich and robust. There are a few enhancements that will make it even stronger:

  • overall pie chart – The “breakdown by time” for phases like script evaluation and layout are available for segments within a page load. As a starting point, I’d like to see the breakdown for the entire page. When drilling down on a specific load segment, this detail is great. But having overall stats will give developers a clue where they should focus most of their attention.
  • network timing – Similar to the issues I discovered in Firebug Net Panel, long-executing JavaScript in the main page blocks the network monitor from accurately measuring the duration of HTTP requests. This will likely require changes to WebKit to record event times in the events themselves, as was done in the fix for Firefox.
  • .HAR support – Being able to save Speed Tracer’s data to file and share it is great. Recently, Firebug, HttpWatch, and DebugBar have all launched support for the HTTP Archive file format I helped create. The format is extensible, so I hope to see Speed Tracer support the .HAR file format soon. Being able to share performance information across tools and browsers is a necessary next step. That’s a good segue…

Developers need more

Three years ago, there was only one tool for profiling web pages: Firebug. Developers love working in Firefox, but sometimes you just have to profile in Internet Explorer. Luckily, over the last year we’ve seen some good profilers come out for IE including MSFast , AOL Pagetest, WebPagetest.org, and dynaTrace Ajax Edition. DynaTrace’s tool is the most recent addition, and has great visibility similar to Speed Tracer, as well as JavaScript debugging capabilities. There have been great enhancements to Web Inspector, and the Chrome team has built on top of that adding timeline and memory profiling to Chrome. And now Speed Tracer is out and bubbling to the top of the heap.

The obvious question is:

Which tool should a developer choose?

But the more important question is:

Why should a developer have to choose?

There are eight performance profilers listed here. None of them work in more than a single browser. I realize web developers are exceedingly intelligent and hardworking, but no one enjoys having to use two different tools for the same task. But that’s exactly what developers are being asked to do. To be a good developer, you have to be profiling your web site in multiple browsers. By definition, that means you have to install, learn, and update multiple tools. In addition, there are numerous quirks to keep in mind when going from one tool to another. And the features offered are not consistent across tools. It’s a real challenge to verify that your web app performs well across the major browsers. When pressed, rock star web developers I ask admit they only use one or two profilers – it’s just too hard to stay on top of a separate tool for each browser.

This week at Add-on-Con, Doug Crockford’s closing keynote is about the Future of the Web Browser. He’s assembled a panel of representatives from Chrome, Opera, Firefox, and IE. (Safari declined to attend.) My hope is they’ll discuss the need for a cross-browser extension model. There’s been progress in building protocols to support remote debugging: WebDebugProtocol and Crossfire in Firefox, Scope in Opera, and ChromeDevTools in Chrome. My hope for 2010 is that we see cross-browser convergence on standards for extensions and remote debugging, so that developers will have a slightly easier path for ensuring their apps are high performance on all browsers.

9 Comments

Who’s not getting gzip?

November 11, 2009 10:46 pm | 15 Comments

The article Use compression to make the web faster from the Google Code Blog contains some interesting information on why modern browsers that support compression don’t get compressed responses in daily usage. The culprit?

anti-virus software, browser bugs, web proxies, and misconfigured web servers.  The first three modify the web request so that the web server does not know that the browser can uncompress content. Specifically, they remove or mangle the Accept-Encoding header that is normally sent with every request.

This is hard to believe, but it’s true. Tony Gentilcore covers the full story in the chapter he wrote called “Going Beyond Gzipping” in my most recent book, including some strategies for correcting and working around the problem. (Check out Tony’s slides from Velocity 2009.) According to Tony:

a large web site in the United States should expect roughly 15% of visitors don’t indicate gzip compression support.

This blog post from Arvind Jain and Jason Glasgow contains additional information, including:

  • Users suffering from this problem experience a Google Search page that is 25% slower – 1600ms for compressed content versus 2000ms for uncompressed.
  • Google Search was able to force the content to be compressed (even though the browser didn’t request it), and improved page load times by 300ms.
  • Internet Explorer 6 downgrades to HTTP/1.0 and drops the Accept-Encoding request header when behind a proxy. For Google Search, 36% of the search results sent without compression were for IE6.

Is there something about your browser, proxy, or anti-virus software that’s preventing you from getting compressed content and slowing you down 25%? Test it out by visiting the browser compression test page.

15 Comments

dynaTrace Ajax Edition: tracing JS performance

September 30, 2009 11:27 pm | 4 Comments

DynaTrace has been around for several years focusing on the performance analysis of backend applications. They entered the frontend performance arena last week with the release of dynaTrace Ajax Edition. It’s a free tool that runs in IE as a browser helper object (BHO). I tried it out and was pleased. It’s important to have development tools that work in IE. I love Firefox and all its add-ons, but I also know how important it is to test on IE and more importantly to be able to debug on IE.

Once you’ve downloaded and installed DAE (my shorthand name for dynaTrace Ajax Edition), don’t be fooled if you don’t see an easy way to start it from within IE. You have to go under Programs in the Start Menu and find the dynaTrace program group. Entering a URL for the first test run is obvious. For subsequent runs, click on the “play” icon’s menu and pick “New Run Configuration…” and enter a different URL. One of the nice features of DAE is that it can run during a multi-page workflow. You can enter the starting URL, and then navigate to other pages or launch Ajax features while DAE monitors everything in the background. When you close IE you can dig into all the info DAE has gathered.

DAE gathers a ton of information on your web app. My main issue is that there’s so much information you really need to play with it for awhile to discover what it can do. This isn’t a bad thing – slightly challenging at the beginning, but once you know how to navigate through the UI you’ll find the answer for almost any performance or profiling question you have. I kept finding new windows that revealed different views of the information DAE had collected.

The main differentiator of DAE is all the JavaScript profiling it does. Time is broken out in various ways including by event triggers and by JavaScript API (libraries). It has an HTTP waterfall chart. A major feature is the ability to save the DAE analysis, so you can examine it later and share it with colleagues. There are other features that are more subtle, but pleasant to run into. For example, DAE automatically UNminifies JavaScript code, so you can look at a prettified version when debugging a live site.

When it comes to analyzing your JavaScript code to find what’s causing performance issues, dynaTrace Ajax Edition has the information to pinpoint the high-level area all the way down to the actual line of code that needs to be improved. I recommend you give it a test run and add it to your performance tool kit.

4 Comments

The skinny on cookies

August 21, 2009 1:56 pm | 4 Comments

I just finished Eric Lawrence’s post on Internet Explorer Cookie Internals. Eric works on the IE team as well as owning Fiddler. Everything he writes is worth reading. In this article he answers FAQs about how IE handles cookies, for example:

  • If I don’t specify a leading dot when setting the DOMAIN attribute, IE doesn’t care?
  • If I don’t specify a DOMAIN attribute when [setting] a cookie, IE sends it to all nested subdomains anyway?
  • How many cookies will Internet Explorer maintain for each site?

Another cookie issue is the effect extremely large cookies have on your web server. For example, Apache will fail if it receives a cookie header that exceeds 8190 bytes (as set by the LimitRequestLine directive). 8K seems huge! But remember, all the cookies for a particular web page are sent in one Cookie: header. So 8K is a hard limit for the total size of cookies. I wrote a test page that demonstrates the problem.

Keep your cookies small – it’s good for performance as well as uptime.

4 Comments

F5 and XHR deep dive

August 11, 2009 1:31 pm | 13 Comments

In Ajax Caching: Two Important Facts from the HttpWatch blog, the author points out that:

…any Ajax derived content in IE is never updated before its expiration date – even if you use a forced refresh (Ctrl+F5). The only way to ensure you get an update is to manually remove the content from the cache.

I found this hard to believe, but it’s true. If you hit Reload (F5), IE will re-request all the unexpired resources in the page, except for XHRs. This can certainly cause confusion for developers during testing, but I wondered if there were other issues. What was the behavior in other major browsers? What if the expiration date was in the past, or there was no Expires header? Did adding Cache-Control max-age (which overrides Expires) have any effect?

So I created my own Ajax Caching test page.

My test page contains an image, an external script, and an XMLHttpRequest. The expiration time that is used depends on which link is selected.

  • Expires in the Past adds an Expires response header with a date 30 days in the past, and a Cache-Control header with a max-age value of 0.
  • no Expires does not return any Expires nor Cache-Control headers.
  • Expires in the Future adds an Expires response header with a date 30 days in the future, and a Cache-Control header with a max-age value of 2592000 seconds.

The test is simple: click on a link (e.g., Expires in the Past), wait for it to load, and then hit F5. Table 1 shows the results of testing this page on major browsers. The result recorded in the table is whether the XHR was re-requested or read from cache, and if it was re-requested what was the HTTP status code.

Table 1. When XHR is cached, what happens when you hit F5?
Past Expires No Expires Future Expires
Chrome 2 304 304 304
Firefox 3.5 304 304 304
IE 7 304 cache cache
IE 8 304 cache cache
Opera 10 304 cache 304
Safari 4 200 200 200

Here’s my summary of what happens when F5 is hit:

  • All browsers re-request the image and external script. (This makes sense.)
  • All browsers re-request the XHR if the expiration date is in the past. (This makes sense – the browser knows the cached XHR is expired.)
  • The only variant behavior has to do with the XHR when there is no Expires or a future Expires. IE 7&8 do not re-request the XHR when there is no Expires or a future Expires, even if control-F5 is hit. Opera 10 does not re-request the XHR when there is no Expires. (I couldn’t find an equivalent for control-F5 in Opera.)
  • Both Opera 10 and Safari 4 re-request the favicon.ico in all situations. (This seems wasteful.)
  • Safari 4 does not send an If-Modified-Since request header in all situations. As a result, the response is a 200 status code and includes the entire contents of the original response. This is true for the XHR as well as the image and external script. (This seems wasteful and deviates from the other browsers.)

Takeaways

Here are my recommendations on what web developers and browser vendors should takeaway from these results:

  1. Developers should either set a past or future expiration date on their XHRs, and avoid the ambiguity and variant behavior when no expiration is specified.
  2. If XHR responses should not be cached, developers should assign them an expiration date in the past.
  3. If XHR responses should be cached, developers should assign them an expiration date in the future. When testing in IE 7&8, developers have to remember to clear their cache when testing the behavior of Reload (F5).
  4. IE should re-request the XHR when F5 is hit.
  5. Opera and Safari should stop re-requesting favicon.ico when F5 is hit.
  6. Safari should send If-Modified-Since when F5 is hit.

13 Comments