Sunday, May 27, 2007

Browser plugins

One of the perks of being a software engineer is that during the day you get to use such phrases as "oh, what the hell", "that's pure crack" and a large number of "expletive-deleted" without it seeming weird in any way. I can only feel sorry for doctors who rarely get the opportunity to scream "oh, what the hell" while looking at new patients. It's quite therapeutic.

I've spent a little time this week implementing support for plugins in WebKit Qt and the general situation of cross-browser plugins is the reason for the above, spot-on, observations. My blog is, of course, famous for hard-hitting political satire but not today. Today I'll talk technology. No politics, hardly any environmentalism, virtually no vegan propaganda - just straight to the point geek talk (well, "write" but "talk" sounds a lot better).

Given how important browsers are in our day-to-day computer usage it would seem that cross-browser plugins should be a well understood and in fact solved problem. Nope, that's definitely not the case here.

So while tapping your fingers on the messy desk, you roll your eyes and ask "so zack, what is wrong with browser plugins?".The role of zack - young, careless and wild wacko is played by critically acclaimed me. The role of "you" is played by "you" and "you" really need to step up because you're not convincing anyone right now.

Currently cross-browser plugins use a very sexy (assuming of course that you're heavily into s&m) mix of Xt and another toolkit of their choice. The usage of Xt is just an utter disaster. In fact mixing event loops in any application is a cause of many subtle problems. George had the best idea when he implemented netscape plugins in an out of process application for Konqueror. It's really the only thing that makes sense.

In the Konqueror when a page has a plugin, an external application, the plugin viewer, is started. That process in turn loads and initializes the actual plugin and via DCOP informs the browser what is its window id. Once the hosting browser knows the window id of the plugin, it can XEmbed it. The neat side-effect of this approach is that if a plugin misbehaves one can kill it without any damages to the current browser session (besides the fact that the plugin doesn't render anymore).

The new plugin standard tries to use XEmbed all way. Unfortunately it assumes in-process communication between the browser and the plugin which again is a bad idea. It's a bad idea because it removes this neat concept of an "event loop" from the equation and assumes that whatever event-loop the plugin uses is the same as the one hosting browser is running. Which in turn doesn't do wonders for the whole "cross-browser" aspect of plugins (as long as the definition of "browsers" includes at least two entries and at least one of them is not Firefox).

So lets say you'd like go nuts (like young people tend to do) and display a slider in your plugin which, of course, good, god-fearing people never do. But lets push forward with this highly dubious example and say that maybe you wouldn't want to go all out and display, maybe not right away a slider but some kind of an animating widget. Well, you're pretty screwed because you need the event-loop native to the toolkit in which the widget is implemented to receive the timing events.. Just ask SwfDec guys. They wrote this awesome piece of software, then wrote a browser plugin using, what claims to be, a cross-browser standard just to find out that in that particular standard "cross-browser" meant that it works in Firefox and Firefox. So SwfDec guys found out that Konqueror is not part of that set (unfortunately they also found out a few other things).

So how could we fix it, without having a large groups of grownups sharing some quiet time in a corner, while the sound of weeping fills the room. Hosting plugins in an external process has clear advantages. To the list that I mentioned above we can add another point - by running a plugin in an external process plugins could easily specify what kind of an event loop they need. If your plugin needs Glib's event loop, GTK+ container would be started, if your plugin uses Qt, Qt container would be started (which might or might not use Glib's event loop in Qt 4) if your plugin needs Xt then you should be punched in the face and it all would just work. Containers would announce their window id's through DBUS so that hosting applications could XEmbed them and the world would be a better place (because clearly "browser plugin woes" are occupying one of the top spots on the list of "what's wrong with the world", right between "dust" and "rich people").

At the moment I'm trying to see how feasible it is to lay npruntime implementation, which serves as a bridge between the plugin and the hosting browser on top of DBUS. If that works then we can move to running all plugins as external processes. Which means:

plugins taking a lot of CPU can be stopped/killed without affecting the browser

when a plugin crashes the browser keeps running without any problems

external event loops don't pollute the browser

a lot of code for plugin viewers could be shared (besides networking interface) between browsers on systems running X11. Which means that for a change plugin working in one browser would run in all of the others using those containers.

This is basically what we're doing in WebKit Qt. I just really want a simply working plugin infrastructure for browsers. I'm not a man who holds grudges, but last year I didn't get what I wanted, even though I've been exceptionally good, so this better work (last year I wanted to win the lottery. Sure, some might point out that I made it exceedingly difficult by not playing (did you see the odds?) but lets not get bogged down by minor details).

Hurah! Roll on plugins that run outside the browser. Currently I have a SUSE 10.1 machine which runs Konqueror 3.5.1 and uses the Flash 7.0 r69 plugin. Browsing to http://www.bbc.co.uk/radio2/ instantly locks up the browser unless plugins are disabled. When your work is finished sites like this won't force people to quit the whole browser. This work can't arrive too soon!

Hey Zack. I totally agree with your post; we (Firefox) would love to have out-of-process plugins too. And we'd love to cooperate on a shared plugin viewer.

Just a couple of issues though:1) there's increasing usage of bidirectional scripting (plugins calling out to browser JS, browser JS calling into plugins). This could mean a lot of context switching. I guess we'll just have to deal.2) we're trying to do support for windowless plugins on X. This is pretty important for platform parity. This is going to mean either shipping an X Drawable across process boundaries (I don't know if that's possible) or doing horrific alpha reconstruction and shipping pixmaps across process boundaries. Maybe there's a third option: give the plugin window an RGBA visual and use XDamage/XComposite on the browser side (in which case we wouldn't really need a specialized windowless mode after all...) None of these options seems immediately appealing. Any thoughts?

Hey, again, thanks for all the comments.As to what's going to be the future of QtWebKit and KHTML, I think I'll write a separate blog about that topic. Nothing is really clear at the moment, but it's certainly not going to be one decision that changes situation of any of them. More an evolutionary process.

Robert: can you send me an email to "my first name" at kde.org ? Maybe we could move the discussion to what-wg list.

I am a designer looking for a way to get widgets to play (dissolve in then dissolve out after 10 seconds) on top of any application. I would also like to dissolve in a different/new widget in the same space where a previous widget just dissolved out.