In the first post we described what WebKit and JavaScriptCore is, how to program a simple WebKitGTK+ application and how to extend the JavaScript functionality with a dumb (empty) class. In the second post, we extended JavaScript to enable desktop notifications. In the third post, WebKit/JavaScriptCore were extended to access UPower properties using D-Bus. And in the fourth post, WebKit/JavaScriptCore were extended to use JavaScript callbacks for UPower methods.
In this article we are introducing another way to extend JavaScriptCore: Seed.
Seed: A JavaScriptCore engine
In previous articles we've seen the power of JavaScriptCore. However, its features are tied to the WebKit engine. Wouldn't be great if we could run standalone JavaScript programs using JavaScriptCore? That's exactly what Seed does. Seed is a JavaScript interpreter, able to run pure JavaScript programs. It uses WebKitGTK+, not to browse, but to run JavaScript code.
Indeed, Seed comes in two flavors:
- As a command line interpreter (/usr/bin/seed).
- As a C library (libseed).
As a command line, Seed is like a Python or Perl interpreter: a script is loaded and executed. However, outside the web environment, JavaScript is pretty much useless. That's why, as we did in our previous examples, Seed adds support for some native libraries. And specifically, Seed can be used to program GTK+ applications using JavaScript, thanks to its GObject Introspection/JavaScript bridge.
libseed, the C library, allows third-parties to easily build scripting capabilities for their programs. It adds a thin layer to JavaScriptCore API. The functions and methods available in JSC are usually available in libseed. This mean we can define new features and access JavaScript objects from C.
A simple Seed script
To install Seed in Ubuntu, type:
$ sudo apt-get install seed libseed-gtk3-dev
This is a simple GTK+ program, which just shows a window with a button.
#!/usr/bin/seed
/* Imports GTK+ libraries */
Gtk = imports.gi.Gtk;
/* Inits GTK+ */
Gtk.init(null, null);
/* Creates window */
var window = new Gtk.Window();
/* Terminates program if window is closed */
window.signal.hide.connect(Gtk.main_quit);
/* Creates button */
var button = new Gtk.Button();
button.set_label('Hello world');
/* Terminates program if button is clicked */
button.signal.clicked.connect(Gtk.main_quit);
/* Add button to window */
window.add(button);
/* Shows */
window.set_default_size(100,100);
window.show_all();
/* Main loop */
Gtk.main();
To run it:
$ seed gtk.js
This shows:
WebViews and Seed
In our previous articles, we've extended JavaScript features manually, programming C methods to create JS classes. Those classes were available to our Web Views. But Seed already provides a lot of libraries. In the next example, we'll see how to use libseed to extend WebKit.
Seed is built upon JavaScriptCore, which is a core element of WebKit. In order to expose libseed features in a browser, we need to connect Seed with WebKit. How to do that? When a WebView is created, a JavaScriptCore's Context Object is created. This Context Object stores the status of the JavaScript engine. Generally, Seed creates is own Context Object, but there is function to use an already created context. Using this function, Seed will populate the WebView's context with Seed's libraries.
This the source code of our program which exposes Seed libraries to a WebKitGTK+ web view.
#include <stdio.h>
#include <gtk/gtk.h>
#include <webkit/webkit.h>
#include <seed.h>
#include <JavaScriptCore/JavaScript.h>
SeedEngine *engine;
static void window_object_cleared_cb(WebKitWebView *web_view,
WebKitWebFrame *frame,
gpointer context,
gpointer arg3,
gpointer user_data)
{
JSGlobalContextRef jsContext = webkit_web_frame_get_global_context(frame);
engine = seed_init_with_context (NULL, NULL, jsContext);
}
static void destroy_cb(GtkWidget* widget, gpointer data)
{
g_free (engine);
gtk_main_quit ();
}
static GtkWidget* main_window;
static WebKitWebView* web_view;
static GtkWidget* create_browser()
{
GtkWidget* scrolled_window = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
web_view = WEBKIT_WEB_VIEW (webkit_web_view_new ());
gtk_container_add (GTK_CONTAINER (scrolled_window), GTK_WIDGET (web_view));
g_signal_connect (G_OBJECT (web_view), "window-object-cleared", G_CALLBACK(window_object_cleared_cb), web_view);
return scrolled_window;
}
static GtkWidget* create_window()
{
GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_default_size (GTK_WINDOW (window), 500, 500);
g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy_cb), NULL);
return window;
}
int main (int argc, char* argv[]) {
gtk_init (&argc, &argv);
if (!g_thread_supported())
g_thread_init (NULL);
GtkWidget* vbox = gtk_vbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(vbox), create_browser (), TRUE, TRUE, 0);
main_window = create_window();
gtk_container_add(GTK_CONTAINER (main_window), vbox);
gchar* uri = (gchar*) "file://webkit-seed.html";
webkit_web_view_load_uri(web_view, uri);
gtk_widget_grab_focus (GTK_WIDGET (web_view));
gtk_widget_show_all (main_window);
gtk_main ();
return 0;
}
To compile it, type:
$ gcc -o webkit-seed webkit-seed.c `pkg-config --cflags --libs webkitgtk-3.0 seed`
And this is the webkit-seed.html file:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<script type="text/javascript">
var sqlite = imports.sqlite;
sql = new sqlite.Database('/tmp/webkit_seed_test.db')
sql.exec('CREATE TABLE test (id INTEGER PRIMARY KEY, data TEXT)');
sql.close();
</script>
</body>
</html>
To run the program:
$ webkit-seed
This time, the browser will appear, but the fun action will be in the /tmp directory. There we'll found a new SQLite 3 database, with a two-field table.
Of course, Seed gives access to a lot of sensible libraries. Safety would be at risk if we let any JavaScript program to run this way, but it also opens a whole world of possibilities!
P.S.: Of course, I tried to run the GTK+ example inside the WebView. Unfortunately, it didn't work :-(
im sure it would appear , great post.
Posted by: software consultant | January 23, 2012 at 05:21 AM