Accessing the Original WebKit API in QtWebKit Hybrid Apps

Everybody wraps WebKit

For each and every port of WebKit, be it Safari, Chrome, QtWebkit, there is always a wrapper around the WebKit object model. These wrappers function as language mappers, e.g., in case of Safari C++ API is mapped to Objective-C, and as a simplification layer around the complex WebKit API. The first case makes total sense, as no developer familiar with Cocoa and Objective-C will like to take an excursion (back) to C++. On the other hand, the Chrome port as well as the Qt port expose a C++ API towards their developers and completely hide the WebKit API despite that this is also already written in C++. Within the C++ universe, I do not understand why the original WebKit API is such a bad bet and must be hidden under all circumstances (I derive this from the fact that everybody hides it)? Is it really that evil?

WebKit API is very stable as it is a reflection of HTML and XML and all the things used in the browser for years and which do rarely change, so a dependency should be acceptable. The only benefit is that the Qt developer works solely with the Qt API or the Chromium developer with a Chromium API. This is ok in case of very simple integration with WebKit, like displaying some external web content in an otherwise traditional application or when implementing a browser. But not in a hybrid application! In a hybrid application I want much more control over the internals of the browser. I do not consider it evil. I consider it as a very nice work horse doing most of the rendering and layout work. I want to reference DOM elements from my application and I want the DOM to interact with my application. I want to replace the excessive use of JavaScript found in Web 2.0 apps with excessive C++ – I do not want JavaScript in a hybrid app, I want C++ only!

In case of QtWebKit, how hard is to get access to the original WebKit API? It is very hard, as the necessary headers are all eliminated from the QtWebKit API and all WebKit symbols are local in the compiled DLL or OS X framework However, I have taken the pain and hacked me into it. The steps required to expose the WebCore API via QtWebKit are:

Get the source tree from Qt git

You will need to build from source. So, get it from http://qt.gitorious.org/qt and switch to the desired branch or tag.

Configure Qt just as normal

configure it but do not run make now!

Adjust the compiler flags for WebCore

run the following commands in the source tree

sed -i -e 's/-fvisibility=hidden//g' src/3rdparty/webkit/{Web,JavaScript}Core/Makefile.*
sed -i -e 's/-fvisibility-inlines-hidden//g' src/3rdparty/webkit/{Web,JavaScript}Core/Makefile.*

(or change these the Makfiles manually) This will compile WebCore in way to expose all internal symbols later in the DLL or framework.

Gain access to the WebKit API

In src/3rdparty/webkit/WebKit/qt/Api/qwebelement.h make QWebElement::m_element public

This is just one way to do it (see later for an alternative)

Build Qt

Now run the normal make; make install

Add WebKit header files and defines to your .pro file

The application code needs to include the WebCore header files.

DEFINES += QT_SHARED ...
release:DEFINES += NDEBUG
INCLUDEPATH += /Users/andre/src/qt/git/src/3rdparty/webkit/WebCore/bridge/qt \
    /Users/andre/src/qt/git/src/3rdparty/webkit/WebCore/page/qt \
    ...

This is a very shortened version to save space here. See the full set of defines in this file: myapp.pri

The order is important. Replace /Users/andre/src/qt/git with the home of your Qt source tree. Some defines are very important and some might be just optional, I just took all of them from the effective compile command when Qt was built.

Include WebCore headers in application code

#include <WebCore/html/HTMLElement.h>
#include <WebCore/platform/text/PlatformString.h>
#include <WebCore/platform/text/CString.h>
#include <WebCore/svg/SVGElement.h>
...

Alternative access to QWebElement::m_element

Instead of changing the visibility of the “m_element” member from private to public, it would also be possible to use a fake subclass in the application code with a public member m_element like here:

class HackWebElement: public QWebElement {
public:
WebCore::HTMLElement *m_element;
};

and then gain access to it by down-casting a QWebElement to a HackWebElement:

QWebElement webElement = ...;
HackWebElement *hwe = (HackWebElement*)&webElement;
// now we can access hwe->m_element

You can also use your preferred method to access a private member in C++ 😉

Using the WebKit API

Now I can do fancy stuff with the WebKit API. Here is an example where I create an SVG element programmatically, i.e., without the need to have WebKit parse some HTML (or XML in this case):

WebCore::QualifiedName svg("svg", "svg", "http://www.w3.org/2000/svg");
RefPtr<WebCore::SVGElement> nel = WebCore::SVGElement::create(svg, hel->document());
WebCore::String s = nel->tagName();
debug(string(s.utf8().data()));
if(nel) {
    debug("it's a WebCore::SVGElement!!!");
}

Conclusion

So far, I have only gained initial access to the internal browser DOM as a first step. This way I can create DOM elements programmatically. In the future, I will experiment with event handlers on DOM objects written in C++ in order handle all UI events in the C++ part of my application. This will reduce the browser part to act as a pure layout and rendering engine which I think should be its sole role in a hybrid application.

Regarding the little “hacky” approach I can say that there is only one place on the whole application code where access to WebKit is gained. This can be in the document, in the frame, or like here in the general element. From then on, no further hacks are required, it is just used and QtWebKit is not needed any more.

I would also wish that in ports like QtWebKit the WebCore API is preserved and exposed to the application developer. I think there are 2 aspects in a WebKit port: One is the physical rendering and display and the other one is the kind how WebKit embeds into the application and is access. Both concerns should be considered separately and I want to be free to consume only the first one and live with the original in the latter one.

Advertisements

7 thoughts on “Accessing the Original WebKit API in QtWebKit Hybrid Apps

  1. Hi,

    first of all thank you for your excellent article, been searching for this information for a while and finally found it! I’ve noticed that you’ve dropped this solution, but I’m still interested to know if it is possible to use QtWebKit into a C++ hybrid application for HTML rendering functionality? My goal is to have an windows win32 c++ application that will have a HTML code composer/editor to generate Template Documents in HTML. For that I will need to have an internal HTML rendering engine, but the compiler that I currently use doesn’t provide one and there is no 3rd party component available for that!

    Should I continue my quest or is this impossible ? Any other suggestions ?

    Regards.

  2. Hi Nelson, I dropped Qt out of the landscape only because QtWebKit would never make it to Apple’s AppStore, but for the task that you’re describing with a clear platform target, Qt is a very good starting point. Qt apps look usually good on Windows. For the HTML rendering part, you couldn’t find something better than WebKit. You can consume WebKit in various incarnations, be it as QtWebKit or Chromium or others. If you need Qt’s signal/slots and you are already familiar with Qt you could go on with QtWebKit (without my hack) if you don’t need signal/slots, then Chromium might be worth a try, as with Google backing it might not be the worst bet. For your task, I guess you will create the HTML in partial or complete tree form, which you throw at the renderer, in this case, if you have a model of the HTML elements in your app, then you can use QWebElement, which is quite generic and in contrast to my hacked approach drops node type information. Regards, Andre

  3. Hi Andre,
    Thanks for the great effort on this.
    What do you think about using only the WebCore/websockets part as a native C++ WebSocket client?
    I haven’t seen any such implementations (the closest being the C libwebsockets, but I’m having a hard time using that with MinGW and wrapping it as a lib for Qt).

    Regards.

  4. Hi Andre,

    Helpful post – are the SED commands still relevant for 4.8.0? First of all the WebKit source has moved to a child ‘source’ directory, and since I can’t follow exactly what they are modifying could you perhaps provide more details?

    • Well the thing is you just need to remove the “-fvisibility=hidden” flags from the makefiles. Otherwise the API will not be exposed outside of the dynamic libs. I have not tested this with any other Qt version than 4.7 but the procedure should be similar, perhaps you need to apply to different folders like you said. You can also change the Makefiles manually if you want. Just remove all those “-fvisibility=hidden” and “-fvisibility-inlines-hidden” flags. That’s what the SED command does on all the Makefiles it finds in the specified paths.

  5. Hi thanks for reply, I did read up on SED and figured out that is what those commands were doing.

    I’m compiling on Windows with MSVC 2010 and the resulting makefiles do not have these flags, I decided they must be relevant to GCC/MINGW compilers only?

    These issue is I have around 32 unresolved externals from JSCore when compiling my QtWebKit based project and including all the headers (modified to suit new directory structure).
    If I link against JSCore.lib, I then get duplicate symbol errors.
    I couldn’t see any declarations on the relevant methods that were different from any other method.
    My C++ skills are pretty basic so I’ve hit my limit on what to try.

    I’ve posted on QTCentre and Qt-Project, and no-one has responded.

    Now I’m modifying QtWebKit to do what I need but that is far from ideal (I don’t mind sharing those changes so happy to use LGPL).

    Might have to pay a Qt consultancy for some answers…

    • Tim, well, from what I read here http://stackoverflow.com/questions/225432/export-all-symbols-when-creating-a-dll it seems to be very complicated to export ALL symbols from the DLLs. I have no idea into what type of libraries QtWebKit compiles under MSVC but I guess it must be some dynamic lib in order to be LGPL compliant. You could try to compile with mingw gcc if possible, then you’d have the options described by me. According to the SO posts, it seems to be hard to export all those symbols via MSVC. But there seems to be the possibility to export at least some of them (the ones you would actually need) using the “module definition file”, see http://msdn.microsoft.com/en-us/library/28d6s79h(VS.80).aspx This is really all greek to me as I have no experience in Windows land. From your forum post it looks like you’re trying to achieve something really cool! But hacking WebKit is really something only few people care about. I can only wish you good luck!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s