{"id":238,"date":"2026-09-11T19:23:31","date_gmt":"2026-09-11T17:23:31","guid":{"rendered":"https:\/\/blogs.igalia.com\/alex\/?p=238"},"modified":"2026-09-14T17:46:12","modified_gmt":"2026-09-14T15:46:12","slug":"wpe-webkit-on-android-now-with-wpeplatform","status":"publish","type":"post","link":"https:\/\/blogs.igalia.com\/alex\/2026\/09\/11\/wpe-webkit-on-android-now-with-wpeplatform\/","title":{"rendered":"WPE WebKit on Android, now with WPEPlatform"},"content":{"rendered":"\n<p>It has been a while since I wrote a post here. The last one was from December 2012, almost fourteen years, which is a bit embarrassing for someone who is still hacking on WebKit. I need to try to fix it, and this post is the first attempt.<\/p>\n\n\n\n<div class=\"wp-block-group is-nowrap is-layout-flex wp-container-core-group-is-layout-6c531013 wp-block-group-is-layout-flex\">\n<p>I would like to write a summary of the work we have been doing in <strong>wpe-android<\/strong> in the last year, where this port of WPE comes from and where it is now. The details deserve their own posts and I plan to write more about specifics. Now that <strong>WPEPlatform<\/strong> is going to be released as stable in the next few days seems a good example of how to use it, we have been actively using to test the API in the last months.<\/p>\n<\/div>\n\n\n\n<p><strong>wpe-android<\/strong> started in 2017 as an experiment of running WPE WebKit on Android. The engine communicated with the system through libwpe, the abstraction library WPE used before <strong>WPEPlatform<\/strong>. It used an Android backend living in its own separate library, libWPEBackend-android, and the Java side exposed the API with the <code>WPEView<\/code>, <code>WPEContext<\/code> and the other proxy classes. It was a useful experiment. It was enough to show the engine running in a phone. The problem is a good part of the Android specific code, in particular the rendering path sending buffers to a <code>SurfaceControl<\/code>, was placed in the application side instead of in the platform layer of the engine. Also Android developers had to learn an API that did not look like anything they already had in the platform, it was close to the Linux WPE API.<\/p>\n\n\n\n<p>Since then we have been implementing <strong>WPEPlatform<\/strong>, a new platform abstraction that has been added inside WebKit. It gave us the chance to fix both problems at the same time. During this spring we implemented a proper Android backend on top of it, so now the display system of Android looks like a regular WPE platform to the engine. <code>WPEDisplayAndroid<\/code> registers as a display extension point and sets up an <code>AHardwareBuffer<\/code> configuration, <code>WPEToplevelAndroid<\/code> follows the <code>ANativeWindow<\/code> lifecycle from the SurfaceHolder callbacks, <code>WPEViewAndroid<\/code> submits the frames with <code>SurfaceTransaction<\/code> and explicit sync fences, and <code>WPEScreenSyncObserverAndroid<\/code> uses <code>AChoreographer<\/code> so the engine renders aligned with vsync. On top of that we added a thin JNI bridge that mirrors the WebKit and <strong>WPEPlatform<\/strong> objects in Java, and a new convenience API that looks like <code>android.webkit.WebView<\/code>, with <code>WebView<\/code>, <code>WebContext<\/code>, <code>WebSettings<\/code>, <code>CookieManager<\/code>, <code>WebViewClient<\/code> and <code>WebChromeClient<\/code>. This way people can reuse the Android documentation and the knowledge they already have instead of learning something new.<\/p>\n\n\n\n<p>We ported the tools used to the new API, the MiniBrowser, the media player sample, the instrumented tests and WebDriver. We were able to delete the legacy stack.<\/p>\n\n\n\n<p>After that, the only thing keeping libwpe in the build was process management: Android cannot simply <code>fork()<\/code> and <code>exec()<\/code>, since auxiliary processes are Android services that need to be launched explicitly. To solve this, we implemented a <code>WPEProcessManager<\/code> API for <strong>WPEPlatform<\/strong> inside WebKit, which landed upstream and is now used by the port.<\/p>\n\n\n\n<p>With that in place, we were able to remove libwpe entirely, and WPE WebKit now talks to Android the same way it talks to any other platform. In parallel, we kept improving other parts of the platform, implementing new API features and fixing bugs along the way. One notable example is a scroll jank issue on the Mali driver, which was resolved largely thanks to the Skia DDL architecture upstream (a story that deserves its own write-up).<\/p>\n\n\n\n<p>There is still plenty work to do, for our small team this project is still a very valuable experiment and we would like to keep exploring the options.<\/p>\n\n\n\n<p>The code is here if you want to give it a try or help:<\/p>\n\n\n\n<p><a href=\"https:\/\/github.com\/Igalia\/wpe-android\">https:\/\/github.com\/Igalia\/wpe-android<\/a><\/p>\n\n\n\n<p>I will try to write about our work around WebKit and wpe-android in more detail in the next weeks, hopefully the next post will not take another fourteen years &#8230;<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large is-resized\"><a href=\"https:\/\/blogs.igalia.com\/alex\/files\/2026\/09\/wpe-screenshot.png\"><img loading=\"lazy\" decoding=\"async\" width=\"461\" height=\"1024\" src=\"https:\/\/blogs.igalia.com\/alex\/files\/2026\/09\/wpe-screenshot-461x1024.png\" alt=\"\" class=\"wp-image-239\" style=\"width:217px;height:auto\" srcset=\"https:\/\/blogs.igalia.com\/alex\/files\/2026\/09\/wpe-screenshot-461x1024.png 461w, https:\/\/blogs.igalia.com\/alex\/files\/2026\/09\/wpe-screenshot-135x300.png 135w, https:\/\/blogs.igalia.com\/alex\/files\/2026\/09\/wpe-screenshot-768x1707.png 768w, https:\/\/blogs.igalia.com\/alex\/files\/2026\/09\/wpe-screenshot-691x1536.png 691w, https:\/\/blogs.igalia.com\/alex\/files\/2026\/09\/wpe-screenshot-922x2048.png 922w, https:\/\/blogs.igalia.com\/alex\/files\/2026\/09\/wpe-screenshot.png 1080w\" sizes=\"auto, (max-width: 461px) 100vw, 461px\" \/><\/a><\/figure>\n","protected":false},"excerpt":{"rendered":"<p>It has been a while since I wrote a post here. The last one was from December 2012, almost fourteen years, which is a bit embarrassing for someone who is still hacking on WebKit. I need to try to fix it, and this post is the first attempt. I would like to write a summary [&hellip;]<\/p>\n","protected":false},"author":3,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5,7,14,10,11],"tags":[],"class_list":["post-238","post","type-post","status-publish","format-standard","hentry","category-igalia","category-technology","category-webkit","category-wpe","category-wpe-android"],"_links":{"self":[{"href":"https:\/\/blogs.igalia.com\/alex\/wp-json\/wp\/v2\/posts\/238","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blogs.igalia.com\/alex\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.igalia.com\/alex\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.igalia.com\/alex\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.igalia.com\/alex\/wp-json\/wp\/v2\/comments?post=238"}],"version-history":[{"count":10,"href":"https:\/\/blogs.igalia.com\/alex\/wp-json\/wp\/v2\/posts\/238\/revisions"}],"predecessor-version":[{"id":278,"href":"https:\/\/blogs.igalia.com\/alex\/wp-json\/wp\/v2\/posts\/238\/revisions\/278"}],"wp:attachment":[{"href":"https:\/\/blogs.igalia.com\/alex\/wp-json\/wp\/v2\/media?parent=238"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.igalia.com\/alex\/wp-json\/wp\/v2\/categories?post=238"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.igalia.com\/alex\/wp-json\/wp\/v2\/tags?post=238"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}