Wt: A Web Toolkit

Writing web applications using a C++ GUI programming style

Dr. Dobb's - February 11, 2008
Wim Dumon and Koen Deforche

From ddj.com

Wim leads Sobicom nv, a software engineering consultancy company specialized in embedded systems design and bioinformatics. Koen is completing a Ph.D. in Medical Sciences. They can be contacted at wdumon@gmail.com and koen.deforche@ gmail.com, respectively.

Wt is a freely available library and application server (www.webtoolkit.eu/wt) that lets C++ programmers write modern web applications using a familiar C++ GUI programming style. Wt then renders the C++ applications to the web browser. Figure 1, for instance, is a running Wt application—a functional look-alike of the GMail composer, fully AJAX enabled, and written entirely in C++ using CSS for the markup.

From a programmer's perspective, the Wt API is similar to those offered by libraries such as Qt, Gtk, wxWindows, and the like. However, instead of rendering widgets to Windows/X11/ windows, Wt incrementally renders the widgets in web browsers. Wt completely hides the underlying web technologies (HTML, AJAX, XML, CGI, JavaScript, and DHTML), chooses a rendering and session-management strategy depending on browser capabilities, and deals with browser dialects.

Browser-side events such as button clicks, mouse movements, and drag-and-drop events are transparently converted into server-side events using Wt's signal/slot mechanism. Wt comes with a dynamic C++-to-JavaScript translation mechanism to avoid the high-latency server roundtrip for simple visual updates, while sticking to a single C++ specification of the event-handling code. While Wt's rendering engine preferably uses AJAX for incremental rendering of updates made to the widget tree, Wt applications also work when AJAX or JavaScript are not available (or disabled). By exposing only a widget-level API, the library can guarantee protection against the most common cross-site scripting (XSS) attacks, by built-in and automatic filtering of displayed strings for malicious tags.

Figure 1: A running Wt application.

Being a native C++ library, web applications developed with Wt typically enjoy greater efficiency and a smaller footprint than Java or Ruby solutions. As such, Wt lends itself to devices where efficiency and footprint matters, like in embedded applications.

Library Overview

A Wt session starts when users access the application URL, and ends when users quit the application explicitly, or implicitly by leaving the webpage. To programmers, session management is transparent and similar to other GUI applications on multiuser systems. In its most naïve configuration, for every new Wt session, a process is spawned and used for the duration of the session. A server with N sessions (logged-in users, for instance), shall therefore have N processes running. Wt can be configured to use cookies or URL rewriting to bind requests to the correct session. With this architecture, all sessions run in their own memory space (a crash of one process can never influence other processes), and sessions cannot communicate with each other unless explicit communication mechanisms are used, providing an additional security safeguard. Because this model is notappropriate or even desired for public web applications, Wt may also be configured to multiplex all sessions onto a limited number or even only one process. This saves server resources and is more resilient to Denial-of-Service attacks. These deployment choices offered by Wt do not affect your program code, but are implemented at linktime or runtime.

Wt Widgets: Interaction with Browsers

Figure 2 illustrates Wt's widget hierarchy. At the top, WWidget exposes methods for organizing the layout and look of each widget. Both the layout and visual aspects are defined in terms of CSS. A drawback is that the layout model of CSS was designed for the markup of documents, and therefore not always intuitive for designing UIs.

Wt's core widgets, which map directly onto HTML building blocks, all inherit from WWebWidget. WWebWidget implementations specify how to render widget changes in terms of the HTML Document Object Model (DOM), and expose all capabilities of these HTML elements. For example, HTML buttons are represented by class WPushButton, HTML text becomes WText, and HTML tables become class WTable. Properties of the HTML blocks (attributes and content) are turned into methods of the classes, named consistently throughout the library. Widgets whose corresponding HTML element supports interaction with the keyboard or mouse, are descendants of WInteractWidget, which exposes these events as Wt::Signal<Event> objects. Form control widgets that may receive keyboard focus and can be enabled/disabled are descendants of WFormWidget, and expose additional events. The library propagates information in two directions between the browser DOM and the server-side widget tree: When the server-side application modifies a widget attribute (color change, enable/disable, or modification of the widget tree), this change is rendered in the web browser (through AJAX, if available). In the other direction, any change or event initiated by users on the DOM is automatically reflected in the widget (such as changes to the edited text of an HTML text input field, which corresponds to a WLineEdit widget). Events are propagated using a signal/slot mechanism (which is a particular implementation of the Observer pattern). This is both for events initiated by user action, such as text changes, mouse clicks, or focus changes, as well as events initiated by application code.

Figure 2: Wt's widget hierarchy.

Next to WWebWidget, there is WCompositeWidget. While WWebWidgets expose as much as possible all capabilities available in their browser-side implementations, WCompositeWidgets implement advanced widget functionality whose implementation details are completely encapsulated. For example, WTreeNode, which defines a node in a tree hierarchy, is internally implemented using WImage and WText widgets laid out in a 2×2 WTable. This internal organization is not exposed by inheriting from WCompositeWidget, and can thus be changed at any point in the future.

The look and organization of a new WCompositeWidget is specified by composition of existing widgets (which may be either WWebWidget or WCompositeWidgets), while the behavior is defined by connecting slots to various signals exposed by these widgets. New widgets are not limited to server-side event handling only, but can use the dynamic C++-to-JavaScript translation feature to bypass the server roundtrip for rendering visual updates (more on this later). For example, the WTreeNode uses this feature to implement client-side expand/collapse toggling.

Deployment Architecture

Communication over the HTTP protocol with a web browser is abstracted in Wt using a Connector implementation. Two Connector implementations are available in Wt 2.x. One connector uses the FastCGI protocol, which lets Wt applications run in conjunction with many web servers via FastCGI plug-ins. A second connector contains a lightweight, built-in web server, so that Wt applications can be developed and deployed without need for a third-party web server.

The open FastCGI protocol was initially designed to allow CGI applications to be long lived and handle multiple requests. A plug-in in the web server forwards requests corresponding to a specific web application through a local socket to such a long-lived process. This process implements a Wt dispatching server, which identifies the session that is targeted by the request and forwards it to the process that implements this session.

On the other hand, using the built-in web server avoids the need for an intermediate protocol by listening for HTTP(S) requests. In such a deployment scenario, a robust web server may still be used as a front-end web server, which is then configured to proxy pass requests to the Wt application. Because of its small footprint, the built-in web server is also a well-suited standalone for embedded applications. Perhaps most importantly, the built-in web server simplifies the development of Wt applications, because you may start them easily in your debugger, and they have a process and thread structure that may be configured to be as simple as a single thread in a single process, which includes the web server.

The World Is Not Enough

Unlike desktop applications, web applications are automatically available to an international audience. To enable this opportunity, Wt provides features to make it straightforward for your application to be localized and internationalized. For localization, Wt provides message resource bundles. A single message resource bundle defines a set of XML files, one for each locale. Each XML file associates localized text or XHTML with message keys.

The Wt API uses WString for all rendered text. WString defines an implicit constructor that takes standard strings (both narrow and wide variants, C and C++), which then represents a literal string. For example:


WText *aText = 
  new WText("Hello World!");

creates a text widget that simply displays "Hello World."

To make a localized version of the hello-world program, you would instead use the static method WString::tr(key), which for convenience is aliased in WWidget::tr(key) to create a localized string:


WText *text = 
  new Wtext(tr("hello-world"));

The string that is displayed will now be resolved by looking for the key in the message resource bundles and the current locale. For example, there could be a file "hello-world.xml" with the contents:


<messages>
  <message id="
    hello-world">Hello World!</message>

</messages>

and a file "hello-world_nl.xml" providing the same contents in locale nl corresponding to Dutch:


<messages>
  <message id=
    "hello-world">Dag Wereld!</message>

</messages>

How is the locale determined? By default, Wt uses the locale that is reported by the browser. But the locale may also be changed at runtime, using WApplication::setLocale(locale). Here comes the nice part of the story. WApplication::setLocale() will invoke a refresh of the entire application, by calling WApplication::refresh(), which will propagate a WWidget::refresh() through the entire widget tree. During this propagation, all widgets use the new locale to resolve their displayed text elements in the new locale, and any changes are caught by the usual Wt rendering mechanism to be reflected in the browser. This propagation requires no effort from you, even for application-defined widgets.

Internationalization requires support for characters that go beyond the 8-bit western character sets. For this purpose, Wt provides Unicode support for both literal and localized strings. For literal strings, you may specify and access content through narrow, wide, and UTF8 encoded strings, and therefore allow interfacing with legacy code. For localized strings, Wt relies on the extensive support for Unicode and different character encodings available in XML.

Server-Side Event Handling

Wt's signals and slots are implemented on top of the Boost.Signals package, which is part of the C++ Boost template libraries. All classes that inherit from WObject can use signals and slots with automatic care for connections when objects are destructed, because WObject inherits from boost::trackable.

Wt makes all user interaction with HTML DOM elements available by emitting the corresponding Wt::Signal. These events include keyboard events (keyWentDown, keyWentUp, keyPressed), mouse events (clicked, doubleClicked, mouseMoved, and so on) and many others. The event is only propagated if the corresponding signal has any connections, and therefore avoids unnecessary communication. Unfortunately, without availability of JavaScript, the application may only react to click events. Wt is designed to be functional even when the client has no JavaScript support, and since the availability of JavaScript is indicated using WEnvironment::javaScript(), the absence of richer interaction may be worked around if required, by providing alternative methods for essential operations; for example, by adding extra buttons.

Listing One illustrates a common Wt construct that instantiates a widget and reacts to some of its events (for brevity, we mix the implementation in the class declaration). In this example, a button is created as part of a new composite widget. When the button is clicked, the method doFumble() gets called, which in this case, disables the button (to prevent the user from clicking twice), and then performs some business logic.

class MyWidget : public WCompositeWidget
{
public:
  MyWidget(WContainerWidget *parent = 0)
    : WcompositeWidget(parent),
      ...
   {
      ...

      fumbleButton_ = new WPushButton("Fumble");
      fumbleButton_->clicked.connect(SLOT(this, MyWidget::doFumble));
      ...
   }
private:
   WpushButton *fumbleButton_;


   void doFumble()
   {
      fumbleButton_->disable();
      
      fumbleSome(...);
   }
};


Listing One

In this example, there is an obvious drawback—the button is only disabled after at least a client-server roundtrip, but also after all the fumbling has been done! This can easily be avoided by using client-side event handling.

Client-Side Event Handling

While server-side event handling can in principle do any kind of event handling, there are limitations because of the latency involved with the client-server roundtrip. When JavaScript is available in the browser, this roundtrip would not be necessary provided that the event handling is specified in JavaScript code. Even ignoring the extra complexity of specifying part of the functionality in JavaScript, and the bad reputation of that language (and its implementations), there are more fundamental problems with specifying part of the functionality in JavaScript. First, an alternative must be provided when JavaScript is not available. Second, the server-side application state is no longer synchronized with the browser-side state. Furthermore, with custom JavaScript, Wt cannot guarantee robustness against XSS attacks and cross-browser compatibility.

Wt provides an attractive alternative, which is a dynamic C++-to-JavaScript translation mechanism for so-called stateless slots in Wt. A stateless slot is any slot that adheres to the contract to always have the same visual effect regardless of application state. For example, to unconditionally disable the button as soon as it is clicked, as it is in MyWidget::doFumble(), no application state or event details are required and therefore the stateless slot learning in Wt may be used to implement this event handling in client-side code.

Consider the code changes in Listing Two. The effect of this code is that the JavaScript code for the MyWidget::disableFumbleButton() slot is learned on the first invocation of the event, and cached in the browser. Thus, the first invocation will require a server roundtrip before rendering the visual update, but subsequent invocations will simply execute the JavaScript again. Obviously, this kind of solution is not sufficient if the fumbling is not something that we expect the user to do repeatedly.


class MyWidget : public WCompositeWidget
{
public:
  MyWidget(WContainerWidget *parent = 0)
    : WcompositeWidget(parent),
      ...
   {
      ...
      implementStateless(&MyWidget::disableFumbleButton);

      fumbleButton_ = new WPushButton("Fumble");
      fumbleButton_
        ->clicked.connect(SLOT(this, MyWidget::disableFumbleButton));
      fumbleButton_->clicked.connect(SLOT(this, MyWidget::doFumble));
      ...
   }
private:
   WpushButton *fumbleButton_;
   void disableFumbleButton()
   {
      fumbleButton_->disable();
   }
   void doFumble()
   {
      fumbleSome(...);
   }
};


Listing Two

With some extra effort, we may also eliminate the roundtrip for the visual update at the very first invocation. By letting the library invoke a stateless slot internally even before it is actually triggered by client-side code, the library may learn the visual changes that are implied by it. To undo the effect of this spontaneous internal invocation, an undo function must be provided.

Applied to the same example, you would change the call to:


implementStateless
  (&MyWidget::disableFumbleButton,
   &MyWidget::undoDisableFumbleButton);

and implement this undo method:


void undoDisableFumbleButton()
{
  fumbleButton_->enable();
}

The library provides stateless implementation for many of its built-in widget methods, such as WWidget::hide() and WWidget::show(), but incidentally also for WFormWidget::enable() and WFormWidget::disable(). Because it is convenient to connect these little methods directly to signals, the client-side optimization is automatically provided with the construct in Listing Three.


class MyWidget : public WCompositeWidget
{
public:
  MyWidget(WContainerWidget *parent = 0)
    : WcompositeWidget(parent),
      ...
   {
      ...
      fumbleButton_ = new WPushButton("Fumble");
      fumbleButton_
        ->clicked.connect(SLOT(fumbleButton_, WPushButton::disable));
      fumbleButton_->clicked.connect(SLOT(this, MyWidget::doFumble));
      ...
   }
private:
   WpushButton *fumbleButton_;
   void doFumble()
   {
      fumbleSome(...);
   }
};

Listing Three