Skip to content

Embedding the Servo Web Engine in Qt Using CXX-Qt to integrate a web rendering engine written in Rust

With the Qt WebEngine module, Qt makes it possible to embed a webview component inside an otherwise native application. Under the hood, Qt WebEngine uses the Chromium browser engine, currently the de facto standard engine for such use cases.

While the task of writing a brand new standard-compliant browser engine is infamous as being almost unachievable nowadays (and certainly so with Chromium coming in at 31 million lines of code), the Rust ecosystem has been brewing up a new web rendering engine called Servo. Initially created by Mozilla in 2012, Servo is still being developed today, now under the stewardship of the Linux Foundation.

With the browser inherently being exposed to the internet, it is usually the biggest attack vector on a system. Naturally this makes Servo very attractive as an alternative browser engine, given that it is written in a memory-safe language.

A Servo WebView

At KDAB we managed to embed the Servo web engine inside Qt, by using our CXX-Qt library as a bridge between Rust and C++. This means that we can now use Servo as an alternative to Chromium for webviews in Qt applications.

From a QML perspective this component is similar to the Chromium WebView, such as providing canGoBack, canGoForward, loading, title, url properties and goBack, goForward methods. The QML item itself acts in the same way with the contents being rendered to match its size.

import QtQuick
import QtQuick.Window

import com.kdab.servo

Window {
  height: 720
  width: 1280
  title: webView.title
  visible: true

  ServoWebView {
    id: webView
    anchors.fill: parent
    url: "https://servo.org/"
  }
}

The screenshot below shows a basic QML application with a toolbar containing back, forward, go buttons and an address bar. We use CXX-Qt to define Qt properties, invokables, and event handlers (e.g. touch events) in Rust and trigger events in the Servo engine. Then any update requests from Servo can trigger an update of the Qt side via the Qt event loop.

As we move towards stabilising CXX-Qt at KDAB, investigating real world use cases, such as exposing Servo to Qt, allows us to identify potential missing functionality and explore what is possible when joining the Rust and Qt ecosystems together.

Technical details

Under the hood most of the heavy lifting is done by our CXX-Qt bindings, which already bridges the obvious gap between the Rust and Qt/C++ worlds. However, some further glue is needed to connect the rendering contexts of Servo to being able to render the surfaces into the actual Qt application. Internally, Servo uses surfman, a Rust library to manage rendering surfaces. At the time of writing, surfman supports OpenGL and Metal, with support for Vulkan being planned.

We use surfman to create a new OpenGL context, that Servo then uses for rendering. To render the result into the QtQuick scene, we borrow the surface from Servo, create a new framebuffer object and blit the framebuffer into a QQuickFrameBufferObject on the Qt side.

Future possibilities

Servo development is active again after a period of less activity, therefore the API is evolving and there is work to improve the API for embedders. This could result in a simpler and documented process for integrating Servo into apps. Also as part of the Tauri and Servo collaboration, a backend for WRY could become available. All of these result in many possible changes for the bridge to Qt, as currently this demo directly constructs Servo components (similar to servoshell) but could instead use a shared library or WRY instead.

On the Qt side, there are areas that could be improved or investigated further. For example, currently we are using a framebuffer object which forces use of the OpenGL backend, but with RHI, developers might want to use other backends. A way to solve this for QML would be to change the implementation to instead use a custom Qt Scene Graph node, which can then have implementations for Vulkan, OpenGL etc and read from the Servo engine.

Alternatively Qt 6.7 has introduced a new QQuickRhiItem element, which is currently a technical preview, but can be used as a rendering API-agnostic alternative to QQuickFrameBufferObject.

If this sounds interesting to your use case or you would like to collaborate with us, the code for this tech demo is available on GitHub under KDABLabs/cxx-qt-servo-webview or contact KDAB directly. We also have a Zulip chat if you want to discuss any parts of bridging Servo or CXX-Qt with us.

Come and see us at Embedded World 2024 where we will have the Servo demo and others on display!

 

Categories: KDAB Blogs / KDAB on Qt / QML / Qt / Rust / Technical

2 thoughts on “Embedding the Servo Web Engine in Qt”

  1. Thank you so much for this, this gives me hope that we could have an upstream Servo Qt web engine. I for one really wish to have Servo as a web engine alternative available in KDE apps (particularly, Falkon) – and even better, if it was the default web engine for it and being able to ditch qtwebengine from my system.

  2. WOW!!! This is absolutely great!!!

    I would love to be able to use this within Falkon or Angelfish and ditch that Google-based-webview-thing for good!!!

    I’ll be really waiting for more news on this!!!

Leave a Reply

Your email address will not be published. Required fields are marked *