Why the Nitro JavaScript Engine Isn’t Available to Apps Outside Mobile Safari in iOS 4.3

Along a similar line to today’s story about the performance differences between Mobile Safari and the system-wide UIWebView control in iOS 4.3, was Tuesday’s mini-brouhaha about web app performance outside Mobile Safari. The Register, as usual, sensationalized it best, in a story headlined “Apple Handcuffs ‘Open’ Web Apps on iPhone Home Screen”:

Apple’s iOS mobile operating system runs web applications at significantly slower speeds when they’re launched from the iPhone or iPad home screen in “full-screen mode” as opposed to in the Apple Safari browser, and at the same time, the operating system hampers the performance of these apps in other ways, according to tests from multiple developers and The Register.

It’s unclear whether these are accidental bugs or issues consciously introduced by Apple. But the end result is that, at least in some ways, the iOS platform makes it harder for web apps to replace native applications distributed through the Apple App Store, where the company takes a 30 per cent cut of all applications sold. Whereas native apps can only run on Apple’s operating system, web apps — built with standard web technologies such as HTML, CSS, and JavaScript — can potentially run on any device.

“Apple is basically using subtle defects to make web apps appear to be low quality — even when they claim HTML5 is a fully supported platform,” says one mobile web app developer, who asked that his name not be used.

The clear insinuation is that web apps running outside Mobile Safari have been made to run slower, but that’s not true. What happened with iOS 4.3 is that web apps (and JavaScript in general) running inside Mobile Safari have been made significantly faster.

The Nitro JavaScript engine is only available within Mobile Safari. Outside Mobile Safari — whether in App Store apps using the UIWebView control, or in true web apps that have been saved to the home screen — apps get iOS’s older JavaScript engine.

Put another way: nothing is slower regarding web apps or web page rendering in iOS 4.3 compared to 4.2 or earlier. If anything, everything is at least a little bit faster. But: the most significant performance improvements in iOS 4.3, particularly for JavaScript, are exclusive to Mobile Safari.

The obvious question: Why? The cynical answer is that Apple seeks to discourage the use of home screen web apps. But if that were the case, why don’t apps from the App Store get Nitro either? Many, many App Store apps use embedded UIWebView controls for displaying web content.

The real answer is about security. Perhaps the biggest reason for Nitro’s performance improvements over WebKit’s previous JavaScript engine is the use of a JIT — “Just-In-Time” compilation. Here’s Wikipedia’s page on JIT. A JIT requires the ability to mark memory pages in RAM as executable, but, iOS, as a security measure, does not allow pages in memory to be marked as executable. This is a significant and serious security policy. Most modern operating systems do allow pages in memory to be marked as executable — including Mac OS X, Windows, and (I believe) Android1. iOS 4.3 makes an exception to this policy, but the exception is specifically limited to Mobile Safari.

It’s a trade-off. Most OSes allow marking memory pages as executable for performance reasons. iOS disallows it for security reasons. If you allow for pages of memory to be escalated from writable to executable (even if you require the page be made permanently read-only first), then you are enabling the execution of unsigned native code. It breaks the chain of trust. Allowing remote code to execute locally turns every locally exploitable security flaw into a remotely exploitable one.

Apple, as of iOS 4.3, trusts Mobile Safari enough to allow this. The upside is that Mobile Safari is now significantly faster. The downside is that any security exploits against Mobile Safari now potentially allow worse things to happen than before.

Web apps that are saved to the home screen do not run within Mobile Safari. They’re effectively saved as discrete apps — thin wrappers around the UIWebView control. (That’s why they show up individually in the task bar, just like apps from the App Store.) Home screen apps may well eventually get access to the Nitro JavaScript engine — Apple simply hasn’t yet done (or perhaps finished?) the security work to allow it. It is not an oversight or a bug, or the result of a single person at Apple wishing to hinder the performance of web apps.

One way Apple could make Nitro available system-wide in iOS would be to do something similar to what they’ve done with web content plugins (like Flash Player) on Mac OS X: execute JavaScript in a separate (trusted) process that maps back to the host app. On Snow Leopard, Flash Player no longer executes within Safari; instead it gets its own process. Similarly, Apple could introduce a dedicated Nitro JavaScript process that executes JavaScript for any app, rather than executing within any app. I have no idea whether this is something Apple is considering or working on, I’m just saying it’s one way they could offer JavaScript JIT compilation to apps system-wide without allowing most processes to mark writable pages in memory as executable.

[Update: And in fact, this is exactly how the still-in-progress WebKit2 framework is designed.]

In short, iOS was designed from the ground up to be more secure than Mac OS X. The price for this trade-off is performance.

Note too, that Nitro isn’t new. The WebKit team first announced it (then known as “SquirrelFish Extreme”) back in September 2008. That it took until now to show up at all on iOS is an indication of how complicated these security implications are. That Nitro’s availability on iOS is limited to Mobile Safari today does not imply that it will always be limited to Mobile Safari.

  1. I’m actually not 100 percent sure that this is true for Android, but my understanding is that every app on Android is running in a JIT. That’s how the Dalvik virtual machine works — and the use of a JIT is the reason why recent versions of Android have performed significantly better than previous ones. I don’t see how they could be using a system-wide JIT if the Android OS disallowed processes from marking pages in memory as executable. But if I’m wrong about this, let me know↩︎