Qt Meets WebKit on OpenHarmony and Oniro
The big news for WebKit on OpenHarmony and Oniro is Qt support. WPE WebKit now runs inside Qt Quick applications, and there is a new demo browser, qtwebkitview-openharmony, to show it.
This post continues my series on porting WebKit to OpenHarmony. Earlier posts covered getting WebKit to compile and render, moving the dependency build to vcpkg and running it on Oniro on the Volla X23. Along with Qt support, this round brings a batch of other changes:
- Upgrade to WPE WebKit 2.54.2
- WebKit for OpenHarmony now builds in the WebKit tree, and the separate vcpkg and Cerbero build repositories are deprecated
- WebGL support
- vsync-driven rendering
- A much cheaper event loop in the UI process
Qt
Until now, the only way to use WebKit on OpenHarmony was through WebKitView, an ArkUI component for apps written in ArkTS. That covers apps built for OpenHarmony from the start, but not applications written in Qt. Qt, and its declarative UI language QML in particular, is very commonly used in automotive and industrial products, and many desktop applications are written in Qt as well. For those, rewriting the UI in ArkTS is a big ask. QML is also an appealing way to write new user interfaces, thanks to its declarative nature and performance.
The Qt Project has been adding HarmonyOS support to Qt, and it mostly applies to OpenHarmony as well. That gives existing Qt applications a path to OpenHarmony and Oniro with their UI intact. Many of them need a web engine at some point, whether for a help viewer, an embedded dashboard, a login flow or a full browser.
WPE WebKit already ships a basic Qt 6 QML plugin, org.wpewebkit.qtwpe, with a minimal WPEView item you can drop into a Qt Quick scene. It is far from a complete web view API, but it was the quickest way to get WebKit running inside Qt on OpenHarmony, so I used it to get things started. From the application’s point of view it looks the same as on Linux:
import org.wpewebkit.qtwpe
WPEView {
anchors.fill: parent
url: "https://wpewebkit.org"
}
Making the QML plugin run on OpenHarmony
Building the plugin was the easy part, since WebKit for OpenHarmony can now be built in the WebKit tree. Its build script, build-webkit-ohos (more on that below), now takes an opt-in --qt <dir> option that builds the QML plugin against a Qt for OpenHarmony build and ships it in the dist/ tree:
Tools/Scripts/build-webkit-ohos --release --qt <qt-ohos-build-dir>
Running it took more work.
Two event loops, one thread
WebKit’s UI process is driven by a GLib main loop. On Linux, Qt’s event dispatcher is usually GLib based too, so the two share one loop for free. Qt’s OpenHarmony event dispatcher is a plain poll() loop, so nothing was iterating the GLib main context that WebKit depends on. Pages simply never loaded.
The fix is a new WPEQtGLibEventLoop, which WPEQtView installs before its first WebKit call when Qt’s dispatcher is not GLib based. It runs a non-blocking GLib iteration whenever Qt is about to block, whenever one of GLib’s file descriptors becomes active (via QSocketNotifier), and when GLib’s next timeout expires (via QTimer). Qt runs main() on its own thread, and that thread becomes WebKit’s main thread. This is the same idea as the libuv pump in the ArkTS WebKitView, just adapted to Qt’s event loop.
Getting frames into the scene graph
WebKit renders web content in a separate web process into OH_NativeBuffers, which cross into the application process over OpenHarmony’s binder IPC as described in the Oniro post. On the Qt side, the plugin imports each buffer as an EGL image through the EGL_OHOS_native_buffer extension and hands it to the Qt Quick scene graph as a texture, with no copies along the way.

A few platform differences showed up here. Qt’s OpenHarmony platform plugin renders with the default EGL display but does not expose it, and there is no DRM device to query, so the plugin uses the default display directly. It also prefers RGBA8888 buffers, and renders at the window’s device pixel ratio so text stays sharp on high-density screens.
Behaving like a browser
Once pages rendered, the demo app exposed a few more gaps that matter for any real application:
- Hidden views kept rendering. A browser with one view per tab would keep every background tab painting. The plugin now unmaps the view when its item is hidden, so WebKit marks the page hidden, stops rendering updates and throttles
requestAnimationFrame. - Favicons.
WPEViewnow exposes the page favicon as aniconproperty, so a tab strip can show it.
Qt itself
Qt also needed a few fixes in its HarmonyOS platform plugin to run on OpenHarmony devices: device type handling, window APIs that return different error codes on OpenHarmony, vsync and buffer format. Without them, applications abort at startup or show a black window. Qt’s packaging also targets HarmonyOS and DevEco, so the demo uses a small deploy script that builds, signs and installs the HAP with the OpenHarmony SDK instead.
Qt WebKitView
To tie it together, qtwebkitview-openharmony is a small Qt Quick browser with tabs, bookmarks and a start page, written entirely in QML around WPEView. It packages Qt, WebKit and their plugins into a single HAP that runs on both the HiHope development board and the Volla X23 with Oniro. The README walks through building Qt for OpenHarmony, building WebKit with the Qt plugin, and deploying to a device.



There is a demo video later in this post.
Under the hood: the rest of the update
WPE WebKit 2.54.2
The port started out as patches on WPE WebKit 2.50.0 and has now been carried forward to 2.54.2, the latest stable release. The OpenHarmony patches live on the wpewebkit-2.54.2-ohos branch, and older branches are no longer supported.
Building in the WebKit tree
Previously WebKit was built as a vcpkg port in a separate repository, from a patched release tarball. That made day-to-day WebKit development awkward. Now the OpenHarmony port builds the way the Windows port does: vcpkg runs in manifest mode and provides the dependencies, and WebKit itself is a regular CMake/Ninja build. The manifest, overlay ports and triplets live in WebKitLibraries/ohos, and one script does everything:
git clone --depth 1 -b wpewebkit-2.54.2-ohos https://github.com/KodeGood/WebKit.git
cd WebKit
export OHOS_SDK_ROOT=${OHOS_BASE_SDK_HOME}/23
Tools/Scripts/build-webkit-ohos --release
As a result, the webkit-openharmony-vcpkg and webkit-openharmony-cerbero build repositories are deprecated and no longer supported.
WebGL
WebGL was on the “not yet supported” list in every previous post. It now works through ANGLE’s EGL backend. ANGLE needed to be told to use the EGL device type, to load the NDK’s libEGL.so, and to resolve entry points from OpenHarmony’s EGL and GLES wrapper libraries rather than straight from the vendor driver. That last one is a close relative of the GL resolution issue I hit on Oniro: on OpenHarmony, which library a GL function pointer comes from matters a lot.
vsync and a leaner event loop
WebKit now paces frames from OH_NativeVSync instead of a 60 Hz timer. Average frame rates barely moved, because the pipeline still needs two to three vsync periods per frame, but frame pacing is much steadier: the worst stall while scrolling went from 174 ms to under 58 ms.
The bigger win was in the ArkTS WebKitView’s libuv/GLib message pump. It was re-arming its libuv timer and poll handles on every iteration, which caused about 2,100 loop wakeups per second with the WebGL aquarium running, of which only around 70 had any work to do. Fixing that brought the UI process main thread from 48% of a core down to 11%, and an idle page now uses no measurable CPU.
Demo
Here is Qt WebKitView running on Oniro on the Volla X23.
Clang 15 is becoming a real problem
The OpenHarmony SDK ships Clang 15 with libc++ 15, and WebKit has moved well past it. The wpewebkit-2.54.2-ohos branch now carries a dozen patches that exist only to make WebKit compile with this toolchain: a fallback std::unexpected because <expected> is missing, a <source_location> polyfill, -fexperimental-library for <ranges> and <format>, and a restructuring of HashSet, ListHashSet and WeakHashSet because Clang 15 evaluates constraints too early.
None of these are hard individually, but each WebKit release adds a few more, and some touch core WTF templates used everywhere. Rebasing gets slower and riskier every time.
So far I have kept patching and expecting the public SDK to move to a newer Clang. It has not: even the latest OpenHarmony 7.0 SDK still ships Clang 15. Chromium handles this by replacing the SDK’s Clang with a newer one when it bootstraps its build environment. I am considering the same for WebKit. If the next SDK release does not upgrade Clang, build-webkit-ohos will likely start fetching a newer toolchain and use the SDK only for its sysroot and libraries.
Future work
The next round focuses on three things:
- Performance. vsync gave steadier frame pacing, but the rendering pipeline still needs two to three vsync periods per frame. Closing that gap is the main goal, and it benefits both the ArkTS and the Qt view.
- The ArkTS WebKitView API. It covers the basics, but it is still far from a complete WebView API. Making it more feature complete is another priority.
- A dedicated Qt integration. WebKit’s built-in QML plugin was a good starting point, but it is not very complete. I will probably move to a separate, OpenHarmony-specific Qt/QML WPEPlatform implementation outside the WebKit tree, the same way the ArkTS integration is done.
The code is on GitHub:
- KodeGood/WebKit: WebKit with the OpenHarmony port,
wpewebkit-2.54.2-ohosbranch - qtwebkitview-openharmony: the Qt Quick demo browser
- webkitview-openharmony: the ArkTS WebView integration