Background Music

macenstein.com has an interesting article about a slowdown in other applications when a Safari window with a specific set of pages is loaded in the background. What a browser should do when it is the background application is actually extremely complex, and not all of the decisions are as clear-cut as they might seem.

Before I drill into the specifics of what a browser does with background Web pages, I’d like to first state that it’s dangerous to jump to any general conclusions when the sample set of pages is very small. In the example article cited above, the author visited five Web sites and clicked around within them. It’s possible that this pathological behavior is specific not just to one Web site but possibly even to one specific page on that Web site. In order to identify the problem it would be better to try the test again with each individual page.

One might expect that a background browser window would do nothing. However, that is a fairly naive assumption once you take a look at the kind of Web content that exists today. There are many ways in which a browser can still be doing required work even while in the background. Here are some examples:

(1) Animated GIFs
Animated GIFs that are clearly visible in a background window must continue to animate. Especially for animated GIF “movies”, people do switch to other windows or apps and expect to be able to watch the movie still while doing other things. In Safari 2, GIFs animate on separate timers (1 unique timer per individual URL) and so this can get reasonably CPU intensive. The fastest any individual GIF can animate is 10ms, so any single GIF is effectively throttled. However once you get many unique images animating at different rates on a page, you can potentially end up firing timers at a much faster rate. In WebKit nightlies we have improved animation so that there is a single timer driving all of the animations. The unified timer helps control the CPU usage, since updates can be coalesced if they occur very close together.

In both Safari 2 and WebKit nightlies, GIFs don’t animate unless they are being painted somewhere. If an animated GIF becomes invisible, then the animation will pause and no CPU will be consumed by the animation. Therefore all animated images in a background tab will not animate until the page in that tab becomes visible. If an animated GIF is scrolled offscreen even on a foreground page, it will stop animating until it becomes visible again.

Safari’s CPU usage with animated GIFs is very good. (For a while readers of MacNN thought Safari had an “animated GIF problem” because of slowness when typing in forum posts, but that bug actually had to do with the Flash ads at the top and bottom of the page.)

(2) Plugins
Plugins are another area where work can occur while a Safari window is in the background. The Mac plugin framework is really archaic (in Safari and Firefox). Many plugins do animation and work based off being pumped “null events” in which they do processing. The faster you pump these events, the faster animations will occur, and the more CPU will be used. Safari 2 actually throttles these events aggressively to background windows and background tabs.

However what we found when we did this was that users complained about the “slow plugin” problem of background windows. They wanted to be able watch videos or listen to audio through browser plugins and have another app active. In other words users wanted the browser to be working actively for them even while in the background. We have actually changed our behavior in the latest WebKit to only throttle backround tabs. Background windows are no longer throttled so that plugins can animate properly at normal speed. We expect that some users still won’t be happy with this change in the one case of wanting to listen to audio in a background tab, but we had to draw the line somewhere.

(3) JS Timeouts and Intervals
setTimeout and setInterval from JS are the third most common way of doing repeating animation/work. We can’t really avoid responding to timers in background tabs/windows, since Web pages rely on this work actually getting done as the foundation of AJAX Web applications. Safari 2 has a key difference from Firefox and WinIE that I suspect is the root of the additional slowdown described in the article above.

Some Web pages specify repeating timers with an extremely small timeout. In fact they often use the value 0 to mean “Fire as soon as you can.” Safari 2 does not throttle these timeouts, and so a poorly-constructed page that specifies a repeating timer under 10ms will actually hog a lot of CPU. Aggressive timers were actually a problem in Mozilla for a long time before being fixed, and they are currently a problem in Safari 2. WinIE, Firefox, and WebKit nightlies basically error-correct the badly constructed page and ignore timer values of < 10ms by changing them to be 10ms. I suspect the root cause of the issue above is just that one of the pages is hogging the CPU with an aggressive repeating timer, but I am prepared to be surprised. 🙂

(4) Marquees
The marquee element can also animate. By default however it is clamped so that it cannot consume too much CPU (60ms ticks of a clock). An attribute called TRUESPEED can be used on a marquee, however, to tell the browser that it should honor the real animation rate (even if it’s really fast). There is a small (but unlikely) chance that a marquee occurred on one of those pages. I don’t believe Firefox supports the TRUESPEED attribute while we do for WinIE compatibility, so this does mean that certain marquees could consume more CPU in Safari than in Firefox. If so, this would be by design (and would match Internet Explorer for Windows). This attribute rarely occurs on the Web, though, so it is unlikely that it is the cause of any real-world slowdowns.

(5) XMLHTTPRequests
Finally a Web page that seems “loaded” may not be because of AJAX-style asynchronous load requests. Because these requests don’t result in any progress bar or load animation, this work can be very subtle (it can basically be going on without the user knowing about it). However, usually the data fetched by such requests loads quickly and on another thread, so it’s unlikely to significantly interfere with the running of other applications.

(6) The Back/Forward Cache
When everything works as planned, pages that go into the back/forward cache suspend all of their running animations. This includes plugins, marquees, JS timers and animated images. It’s possible that some page got into the B/F cache without being properly paused.

A few other final points to make are:
(1) Browsers rarely get served the same content, even on very popular sites. Without spoofing it’s hard to know if Safari is being served some buggy content from one of the pages in question.
(2) A small sample set isn’t enough to draw general conclusions. Try a bunch of other different Web sites and see if a slowdown still occurs. If so, then maybe there is a systemic problem. Until then, though, all we know is that something is hogging CPU in one of five Web pages.
(3) Reduce reduce reduce! Reduce the problem if possible. Cut it down to one page. Don’t go back/forward (just go right to the pages instead of clicking through to them).

Provide us with precise steps to reproduce and we can verify what the problem is and fix it very quickly. In fact, it may have already been fixed! 🙂