WebKitGTK+, and the Page Cache

So, one of the things I get to do during work hours for Collabora is to contribute code, and do maintenance tasks for WebKitGTK+, and have been doing so since early last year, working on all kinds of things, from improving the network backend to handle the real-world web, to fixing scrolling problems, while reviewing patches from the many awesome developers who have been joining us (more on that later =D).

One of the big features I have worked on this past month or so, along with Xan Lopez is the Page Cache. The page cache is a feature of web browsers that makes going back, and forward between pages in the same view very fast. It’s better explained in this post, but to summarize, the idea is that instead of destroying all the work you have done since downloading the resources, and having to reparse/rebuild the structures the view uses to display the page from the cached resources, you hit pause on the page, and store the whole thing as is, and when coming back to it, you just hit play. You can see in the video two instances of Epiphany, one with the page cache enabled, one with it disabled. Easy to see which was has it enabled. Thanks to KiBi for the suggestion regarding a page that shows this easily =D.

We initially thought we had this feature enabled, since our initialization functions (that exists since before the current maintainers were involved) did setup the number of desired pages in the cache, but during the hackfest we held in December we found out we were fooled all this time. Enabling the page cache does make going back faster, but also made lots of things become unstable and crash.

Since then, we have been working on figuring out all the problems, and fixing them, using help from adventurous users of in-development software ;D. I believe we’re now at a point in which I can happily declare the GTK+ port has a working page cache in trunk! If you’re interested in the nasty details, bear with me!

Let me go back in time a bit, and show you what problems we had. First, some background: the GTK+ port deviates a lot from the other ports when it comes to scrolling. This is because, when designing this part of the port, Holger Freyther had a very nice idea in mind: that the WebView should be a first-class citizen GTK+ scrollable widget. Meaning it would use GTK+’s adjustments for scrolling, and be able to interact with any parent scrolling widget, be it a GtkScrolledWindow, or a MokoFingerScroll.

We cannot just throw away all the rest of the scrolling code in WebCore, though, that deals with all the details related to interacting with the DOM, and JavaScript code. This means our WebView contains adjustments that need to be set, and unset on our port’s version of WebKit’s own representation of the view, called the FrameView, to interact with it, and to get updates on the bounds of content, and such. For every load, in the non-page-cache case, a new FrameView is created, the previous one is destroyed – this means we need to set the adjustments on every load.

The problem starts when you have the page cache enabled, because the code path used to do what is called “commit” the load of a cached page (that is, start replacing the content that is currently being displayed by the one that should now be displayed) is completely different, and we were not setting the adjustments on this new view, so we started with that.

But all was not well. We were still having weird behaviour with scrollbars disappearing, and becoming the wrong size, and worse, crashes when “back” was hit. We then started investigating in more detail how it is that the page cache does its magic, to try and figure out the source of all evil.

It turns out that when you leave a page that can be cached, the existing FrameView is no longer destroyed – it is stored as is in a CachedFrame to be restored if you go back, and a new one is created for the new page. This was having the undesired effect of having the adjustment be set in more than one FrameView at once, causing all kinds of (predictable, after we knew for real what was going on) unwanted effects. Thus, we reworked the code to make sure the adjustments are only ever set in one FrameView at once, making sure they are unset when the FrameView is being frozen, and reset when it’s being restored from the page cache.

Last, but not least, it was discovered that going back to a page that contained resources with data: URIs (such as Google results pages which contain a small number of image hits) also caused a crash. This was because our network backend was not storing the data: URI in the ResourceResponse objects it fed into WebCore. The page cache relies on those responses to recreate the requests it uses to artificially replay the load when restoring the page from the page cache, so we fixed that as well.

What can be taken from all this? Building browsers is a lot of hard work! I can’t think how we could deal with this level of complexity without the awesome testing suite of WebKit. The good news is all of those issues I talked about in this post are now covered by the automate tests that run as part of the normal buildbot cycle in our bots, so we’re covered for the future, at least for these specific problems =D.

Content-Encoding in soup – all your gzip are belong to us

One thing everyone forgot to talk about the WebKitGTK+ hackfest was that master Dan Winship added basic Content-Encoding support to libsoup, and patched WebKitGTK+ to use it. If you are using a recent enough version of those you will finally be able to visit web sites that send gzipped content despite the browser saying it could not handle it, like the Internet Archive.

This was one of those cases in which the web shows all of its potential to behave weirdly. The HTTP/1.1 RFC says that if an Accept-Encoding header is not present, the server MAY assume the client accepts any encoding, so we were having many sites send us gzip content even though we did not support it. We then started sending a header saying “we support identity, and nothing else!”.

It turns out the web sucks, so many servers were not happy with a full header, and started giving us angry looks (slashdot, for instance, would not render correctly because it started sending encoded CSS files!). We then simplified the header we were sending, which made those servers happy again. Some sites, though, completely ignored our saying we didn’t support anything except identity, and sent us gzipped content anyway. Most of these were misbehaving caches (this was the case for Wikipedia), so would work after you asked for a forced reload, which would ignore the cache, but some servers, such as the Internet Archive’s didn’t really want to talk about encodings – they only wanted to send gzip-encoded content.

So, in the end, our only way out was implementing the damn encoding support, which finally happened during the hackfest. Take that, web!

WebKitGTK+ HackFest!

The WebKit hackfest is now over, and I think it was a very productive week. Thank you very much to all who attended, to Igalia for organizing the hackfest, and hosting us so well, to Collabora for having sponsored the event, and allowed me to spend the week working on it, and to the GNOME foundation for having payed all of my costs!

Xan blogged about day 0, and also a summary of all that was done, so I’ll focus on the stuff he forgot to mention ;D. The hackfest, for me, started on day -1 with me not allowing Xan to go sleep before he had reviewed a couple patches of mine to fix DOM context menu handling. It always bothered me that Epiphany failed to open right-click menus in some pages, or let pages handle the right click. Well, this is fixed now, and Zimbra users can now have their right click menus, and WoW players can remove talent points from their calculators =P.

It turns out that many of the attendees don’t like pages messing with their context menus, though, and they had some good points to back up their positions (like pages making it hard to save images, for instance), so I implemented a way to force openning the custom menu: Ctrl-rightclick.

We wanted to use a GtkInfoBar to display questions regarding the form saving – our initial implementation always saved all credentials, but that didn’t sound good enough. Xan and I thought it would be very complicated to make this work, because there were assumptions in the code regarding which widget contains which, but it turned out to be quite trivial – making EphyEmbed a descendant of GtkVBox instead of GtkScrolledWindow, fixing a small number of assumptions, and that was it.

The passwords are saved in the GNOME Keyring. It’s interesting to point out that GNOME Keyring seems to be unhappy with the number of passwords a browser stores – Xan’s daemon was hanging, crashing, and spawning a large number of threads. My daemon decided to take up some 300MB of RAM at one point. It’s somewhat funny to see how much a browser pushes the limits of our platform. We are hoping this will improve with the new keyring APIs, and the rewrite that is ongoing. It’s nice to see my browser form passwords in seahorse, though, and be able to manage them like any other.

One more thing worth of notice, although this post is already a bit too big: one of the main concerns people had during the Hackfest was on making build time smaller. Touching a single file in WebCore causes a debug build of 10 minutes on my laptop. Evan Martin and Benjamin Otte made a push at removing unnecessary includes from WebCore, and WebKitGTK+ files, which brough the build time down a bit. They end up inspiring Aroben, from Apple, to go even further into this, and remove many includes from files all over WebKit.

Evan was also able to bring linking time down by making it possible to link libwebkit without having to build all the intermediate libraries, which brought build time down to 1 minute, when touching a single file in WebCore. Behdad and I also started looking into breaking WebCore up into lots of shared libraries for Debug builds, since we don’t care too much about speed penalties in those. None of these experiments got committed yet, but I am hoping we will be having a better time hacking on WebKitGTK+ in the near future.

It was awesome meeting everyone, by the way! See you around =).

Regressions, ah, regressions

There are few things I really hate. One of them is regressions. Regressions are bad because they usually take away things we are used to rely on, and leave us with the idea that perhaps the technical improvements didn’t really improve our lifes as a user, despite putting less burden on the developers. Software is made for users, after all.

As part of my work on WebKitGTK+, I always keep an eye on regressions, both from previous WebKitGTK+ releases, and those imposed on embedding applications on their migration away from Gecko, and try to focus some of my efforts into lowering their numbers, whenever I can.

In recent times I have worked on removing a few very user-visible regressions in Epiphany, which I see as the most demanding WebKitGTK+ user in GNOME, such as save page not working, missing
favicon support, failing to
perform server-pushed downloads (such as GMail attachments), and not being able to view source. An example of a regression from a previous version of WebKit also exists: in 1.1.17 we started advertising more than we should as supported by the HTML5 media player, causing download to be almost completely broken.

All of these are working if you are using WebKit and Epiphany from trunk/master, so should be on the next development versions of WebKitGTK+ and Epiphany. Other people have also fixed many other regressions; a few examples: Xan has reimplemented the Epiphany customization of the context menu, Frederic Peters provided a work-around for mailto: links while we don’t have SoupURILoader yet, and Joanmarie Diggs keeps rocking on the accessibility front!

If you find regressions, keep them coming! If you have a patch, even better! =)

Next week WebKitGTK+ team gets together to work furiously on improving WebKitGTK+ in a hackfest sponsored by Collabora, and Igalia, and hosted/organized by Igalia. While there I should also get my hands on one of these. Can’t wait! =)

You know what, this clutter thing is cool

One of my awesome colleagues from Collabora, Danielle Madeley, has done various improvements to the clutter-gtk project started by the also awesome Alexander Larsson. Reading her blog I was so impressed with this post, that I decided to spend some free time to try out some crazy, experimental stuff using that.

What I did was create a very simple GTK+ widget that derives from GtkClutterEmbed, and works as a somewhat replacement for GtkNotebook, called, proving how bad I am at naming things, GkOverview. Like I said, it’s not really stable or well-done, it doesn’t even free its resources (in fact, it doesn’t even have implementations for finalize and dispose!), it’s really just an experiment.

What GkOverview does is provide a simple API for you to append widgets into it, and it is able to show you one of those widgets, or an overview of all of them. This is quite simple, and yet very powerful. With the help of my significant other, I have got a layout of the widgets in the overview that I really like.

Of course I used WebKitGTK+ to try it out, what else? And since I had effectivelly, at least in my head, created a fairly convincible replacement for GtkNotebook functionality, I decided a second challenge I could take on myself was to make my preferred browser, Epiphany, use that instead of its EphyNotebook widget. Epiphany being quite well-designed, replacing EphyNotebook was quite a breeze, and here’s the result!

Before I go on, let me repeat it: this is all crappy, experimental, curiosity-induced work. It may be that in the future we can use stuff like this to make, say, improving the back/forward mechanism, history navigation, as other browsers do, replacing tabs with a better UI mechanism, and whatnot.

There’s a somewhat big “video”, for your pleasure (I was finally able to create a nice video, using Byzanz =)).

WebKitGTK+ 1.1.14 is here!

1.1.14 is definitely worth blogging about. While I was updating the gtk-doc stuff to update the documentation that is available in our website I was thinking that this is probably the release with the largest amount of API addition I have seen in my time as a contributor to the project! Before I start talking about the new APIs, though, let me talk of something that went under the hood: Xan has patched WebKitGTK+ to override libsoup’s connection limits, so that our limits match those of other modern browsers – we now allow up to 60 connections total, and 6 per host. People have been reporting load performance improvements for some sites!

We have an initial set of APIs to deal with the data that was loaded to render the page, represented by the new DataSource object. This allows you to peek at the source of the HTML that was rendered, for instance. In the near future we will have ways of getting the data and other information for each resource that is downloaded to compose the page. Jan Alonzo has spent quite some time studying, implementing and perfecting this API, with some help from me and Xan.

One initial piece of the larger API to control resource loading is already there, as well. The WebView will use the new resource-request-starting signal to let you know that a request is about to be sent, and allow you to modify headers, or even the URI that is going to be fetched. For some, the greatest news here is that this allows a basic implementation of AD blocking, which is now supported in Epiphany with the WebKit backend, as of version 2.27.92, released today! This is something I have been looking at in my Collabora time for some time now, and working along with Xan and Jan, we finally seem to have figure out the API interactions correctly. There’s more to come regarding resource loading tracking, in the near future.

That would already be a lot, from my point of view, but there’s a whole lot more. A new contributor, Martin Robinson, has arrived proposing new API pieces for an important HTML5 tool: web databases, which brings with it the concept of SecurityOrigin. Jan has exposed API to put the WebView in “view source” mode, Xan has given us undo/redo support for editable content, and Zan Dobersek has done some overall improvement of our HTML5 media player.

That’s a lot of change for a release, which also goes to show we still have quite a gap to fill, but we are being able to move forward and fill them quite quickly. If you want to help out, pick something in your area of interest, and send us patches!

WebKitGTK+ progress, and a new browser!

Long time, no blog. I have apparently been slacking for a very high number of versions, now, so I better start doing something about it!

My last blog was about 1.1.7, and we are now at 1.1.11, so let me try to get a quick summary of important changes since 1.1.17:

  • Lots of fixes have been made on the scrolling code, which now behaves more like you would expect from a GTK+ widget, and notifications to web applications are also working now
  • Thanks to the awesome zecke, you can embed arbitrary GTK+ widgets using the <object> and <embed> tags, using the create-plugin-widget signal
  • WebKitNetworkRequest received a lot of the love it was asking for, and is now very useful for tracking the HTTP conversation
  • Xan has done awesome work on a11y, and we may have enough done before 2.28 to make WebKitGTK+ be accepted, but that remains to be seen; if you care about that, now is a good time to go to https://bugs.webkit.org/show_bug.cgi?id=25531 and help out!
  • XSS auditor, a new feature built to improve WebKit’s security is now enabled
  • Copying and pasting has seen many improvements
  • WebKitGTK+ no longer has hackish content sniffing, since we now have that implemented into libsoup!

In other news, WebKitGTK+ seems to have gained another application for its ecosystem: Uzbl, which promises to be a good browser for people who like to work with the UNIX way. It doesn’t really appeal to my GNOMEr ‘I like things that just work’ heart, but it looks like a tool that may appeal to people who prefer building their work environments from various different pieces, so that it works exactly like they want to. I am very happy to see WebKitGTK+ is making it possible for people to write such tools!

My first patch to WebKitGTK+ committed!

Well, not really my first patch. But the first thing I tried to mess with when I first started looking at WebKitGTK+ was the WebKitNetworkRequest object, because I was fancing the idea of writing stuff such as HTTP transactions monitoring, and things like that. So I wrote a big patch which exposed the internal WebCore object (ResourceRequest) fully through our own object. That was back in early 2008. We have come a long way since, and through all these months I got a broader perception of what kind of APIs we need, and how WebCore works. We also decided on going soup-only, which had a huge impact on what the final patch actually looks like.

The patch which finally got committed this week is, how can I put it, VERY different from what I had originally written. You can take a look at the long discussions about it in the bug report I used to track progress. I think I should point out that Marco Barisione and Christian Dywan were crucial in helping me get going with my contribution to WebKit at that time.

What this change gives us is basically the fact that a WebKitNetworkRequest now carries more than just the URI for the request (it actually carries with it a reference to the SoupMessage that will be used later in the request processing, which we are planning to expose in the near future), meaning that when WebKit API gives you a request, and you use it to cause a new load (for, say, opening in a new tab), you still get all the headers that were supposed to go with the request, so you don’t lose things such as, for instance, Referer. So, now, after more than 5 years, the bug that complained that Epiphany did not set Referer (and Galeon before that) for new tabs is finally closed.

By the way, this problem has been fixed for Mozilla’s browser back in 2002, but the embedding API is still buggy up to now. There is still hope, since there’s an attached patch that fixes the issue to be reviewed, and landed. If anyone is reading, it might be a good oportunity to get this fixed in there as well, so that users of applications that use Gecko’s embedding API can also benefit!

Epiphany/WebKitGTK+ in Debian unstable

I have prepared an epiphany-webkit source package some time ago, and it has finally got out of NEW, thanks to the work Ganeff did this weekend on processing the queue =).

The good thing about those packages is I have patched them heavily to allow for easy parallel installation with Epiphany/Gecko, so you don’t need to give up your current browser to experiment and test Epiphany with the WebKitGTK+ backend. The gconf tree used for this package is /apps/epiphany-webkit, separate from the normal /apps/epiphany, for extra safety, but notice that your ~/.gnome2/epiphany will be shared between the Gecko and WebKit versions, even though the files used by each of them are different most of the time.

Go ahead and install the epiphany-webkit package, and have fun. Notice that if you have an already running session of Epiphany/Gecko, running epiphany-webkit will not be enough to launch Epiphany/Webkit, since Epiphany will just request that a new window be opened through D-Bus. The easiest way to test, if you just want a quick peek, is to run epiphany-webkit -p; this will run a ‘private’ instance of Epiphany/WebKit, which doesn’t touch your history, bookmarks, and passwords.

If you are feeling adventurous and want to make Epiphany/WebKit your default Epiphany you can do so using the following command, and selecting epiphany-webkit:

# update-alternatives --config epiphany-browser

WebKitGTK+ 1.1.7!

So WebKitGTK+ 1.1.7 is here. JIT is finally enabled for x86-64 on Linux! Yay! Go get it!. I am hoping for 1.1.8 we will have serious improvements on what we have landed recently, and on the Media Player. So, stay tuned =D. It looks more and more like we will have a good release for GNOME 2.28.