O'Browser : an OCaml in a browser
Introduction and tutorial

Benjamin Canou

With the help and support of my PhD thesis advisors :
Vincent Balat and Emmanuel Chailloux

Abstract: We present a way to run OCaml programs on a standard, unmodified web browser. To achieve this, we designed a bytecode interpreter in JavaScript, as well as an implementation of the standard library. Since the web browser does not provide the same interaction mechanisms as a typical OCaml environment, we provide a modified standard library, enabling interaction with the web page. This page is a presentation of the project, as well as a tutorial on how to write and run your own O'Browser programs.

Overview

What is in the package ?

The Virtual Machine

The VM is written as a bunch of JavaScript files, which are linked together by the C preprocessor (cpp). The built VM is the vm.js file, which is to be added to web pages using O'Browser by a marukp.

The main function is which takes an OCaml bytecode file url as first parameters, followed by string arguments, available from Sys.argv from the O'Browser program. Since OCaml bytecode files use bytes, and JavaScript is only able to load (at least on some engines) 7-bit ascii text files, the bytecode files are to be encoded by uuencode.

For example, the tutorial infrastructure is loaded after the page is rendered by the following code :

The modified standard library

O'Browser programs are to be linked with our altenative standard library, which is the one of OCaml 3.10 stripped down from all console Input/ouput functions (like print_string, etc.). It includes the Thread module, and the Graphics module (which provides an alternative open_graph function returning a DOM node to be included where you want the Graphics window in the page). The later one uses the soon-to-be-normalised HTML5 canvas element, which is only available on recent Gecko, Opera and WebKit based browser (i.e. not on MSIE and Konqueror 3).

The supplementary Js module provides interaction with the DOM (the Document Object Model).

The implementation of the externals is coded in rtjs.js

A few examples

Licences

O'Browser is Free software, see the README file for license details.

Tutorial

Warning: This tutorial expects you to know the OCaml language. Knowing HTML is required but not JavaScript. The examples have been tested successfully on Firefox 3 beta, Opera 9.5 beta, Safari 3.1 for windows, Internet Explorer 8 beta and Konqueror 4 (for the two later ones, the JavaScript engines were quite slow at running this prototype on some examples during our tests, this may change in the future).

Hello World

Before trying to compile the examples, you have to compile the O'Browser distribution itself. For this, you need OCaml 3.10 and the GNU version of make, then you just have to type make in the top level folder of the extracted distribution package.

A «Hello World» in OCaml in your browser

The Js module, provided in the modified standard library contains a binding of the JavaScript function alert wich opens a simple dialog showing its string parameter and a «OK» button.

Please note that such a popup blocks the entire JavaScript execution flow and so prevents other OCaml preemptive threads to run until the user clicks on «OK».

To compile the example, use ocamlc with the environment OCAMLLIB set to the path where the alternative runtime library has been built (the rt/caml folder of the distribution). You then have to uuencode your file. For example, on a UNIX-like system with a bsh-like shell, you would do something like :

CAMLLIB=$OBROWSER_PATH/rt/caml ocamlc hello.ml -o hello.exe uuencode hello.exe stdout > hello.exe.uue

If you named the uuencoded file hello.exe.uue, you can then copy or link the vm.js file in the same folder and add the following hello.html page to run your Hello World :

A «Hello World» using the DOM

The Document Object Model (DOM) is an internal structure of the browser, in which each element of a web page is represented by a node. A node encapsulates named properties, defining its appearance, behaviour, etc. Each node of the page is bound to a JavaScript Object, and the document can be read and modified by interacting with the properties of the JavaScript objects.

For example, one can modify the children property to add content to a page element. In this example, we get a node identifier (a string) as Sys.argv.(1). We then use get_element_by_id to retrieve the associated DOM node (bound to the Js.Node.t type in the OCaml bindings). Finally, we append a text to the content of the node.

An interactive «Hello World»

Some JavaScript objects have special properties which can contain closures, called when some event occurs. For example, the a DOM element can have a special behaviour by setting a closure to its onclick property.

The OCaml DOM interfaces provides a mechanism to run a OCaml closure on a DOM event. In JavaScript, when an event is run, the JavaScript control flow is blocked until the callback returns. The OCaml closures spawned by events are, on the contrary, running concurrently with others, so the programmer doesn't have to put explicit yields and can run complex computations within event handlers.

A color picker

Modifying content by browsing the DOM

Warning: Please run these examples before having run any source code coloration since they browse the whole DOM and source code coloration generates an enormous amount of DOM nodes.

Constructing a TOC from the DOM

This examples browses the whole DOM of the page, numbers the sections and extracts the TOC. To do this, it matches the attributes tagName of the node, increments its counters when it find an "H1", "H2" or "H3", and does a recursive call on every Js.Node.children of the node. It also adds anchors to the page and links the corresponding TOC items to them.

Modifying links behaviour

With this little example, one can add links on a page, wich target images through their thumbnails. When the user clicks on a thumbnail, the image is displayed inline if the user has JavaScript enabled, by modfying the links' targets and events.

A flower Another flower
Another flower Another flower
Try the images above before and after having run this example.

Events and preemptive threads

The JavaScript runtime provides concurrent preemtive threads, with all the primitives of the standard OCaml Thread library (excepted the ones for IO).

A timer

Here, the program lauchnes a thread with a function which updates the display and sleeps one second before calling itself. In JavsScript, in order for the user to be able to interact, the programmer would have to retrun from the function, and to enqueue a call to the display function in the event queue using window.setTimeout. Moreover, there is neither sleep, nor call/cc mechanism, so the programmer has to fill in manually the environment of such a continuation. The provided preemptive threads enables us to write such an exemple with a simple recursive function that never terminates.

Producer & consummers

Mutexes are also implemented, which enables to write concurrent programs, like the well known producer/consummers example. In this example, four consummers eat the food you feed concurrently. For this, they share a mutex on the food stock (which in fact is a DOM node).

Annexes

Sources of the tutorial infrastructure

Is you look at the source code of this page, you'll see that it only contains the text. Exemples and source code extracts are in fact commands encapsulated in IDs like or .These commands are preprocessed by tutorial.ml and call the examples, as well as several syntax colouring scripts (which explains why the syntax colouring is a bit slow). The Js module provides the function decode_id which gives a list of strings from such an encoded ID, (here escaping :amp: as an ampersand).

Sources of some examples from the package

Valid XHTML 1.1 Valid CSS