In the previous 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 class. Now is time to show add some real features.
A class for desktop notifications.
GNOME and KDE come with a desktop notification support, so programs display pop-up windows in when certain events happen (i.e. new track in the music player). The library which allows this is libnotify. It declares NotifyNotification, a GObject which represents the pop-up notification. It contains a text and other fields.
To install libnotify in Ubuntu, type:
$ sudo apt-get install libnotify-dev
In the code below, we declare a Notification class in JavaScript, with a constructor and a method called notify to be able to send notification pop-ups from a web page.
#include <gtk/gtk.h>
#include <webkit/webkit.h>
#include <libnotify/notify.h>
#include <JavaScriptCore/JavaScript.h>
/* Class initialize */
static void notification_init_cb(JSContextRef ctx,
JSObjectRef object)
{
/* Inits notify */
notify_init("Notification");
}
/* Notification.notify method callback implementation */
static JSValueRef notification_notify_cb(JSContextRef context,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef *exception)
{
/* At least, one argument must be received */
if (argumentCount == 1 && JSValueIsString(context, arguments[0])) {
/* Converts JSValue to char */
size_t len;
char *cstr;
JSStringRef jsstr = JSValueToStringCopy(context, arguments[0], NULL);
len = JSStringGetMaximumUTF8CStringSize(jsstr);
cstr = g_new(char, len);
JSStringGetUTF8CString(jsstr, cstr, len);
/* Creates a new NotifyNotification. */
NotifyNotification *notification = notify_notification_new(cstr, NULL, NULL);
/* Sets the timeout of the notification. */
notify_notification_set_timeout(notification, 3000);
/* Sets the urgency level of this notification. */
notify_notification_set_urgency(notification, NOTIFY_URGENCY_NORMAL);
/* Tells the notification server to display the notification on the screen. */
GError *error = NULL;
notify_notification_show(notification, &error);
g_object_unref(G_OBJECT(notification));
g_free(cstr);
JSStringRelease(jsstr);
}
return JSValueMakeUndefined(context);
}
/* Class method declarations */
static const JSStaticFunction notification_staticfuncs[] =
{
{ "notify", notification_notify_cb, kJSPropertyAttributeReadOnly },
{ NULL, NULL, 0 }
};
static const JSClassDefinition notification_def =
{
0, // version
kJSClassAttributeNone, // attributes
"Notification", // className
NULL, // parentClass
NULL, // staticValues
notification_staticfuncs, // staticFunctions
notification_init_cb, // initialize
NULL, // finalize
NULL, // hasProperty
NULL, // getProperty
NULL, // setProperty
NULL, // deleteProperty
NULL, // getPropertyNames
NULL, // callAsFunction
NULL, // callAsConstructor
NULL, // hasInstance
NULL // convertToType
};
/* Callback - JavaScript window object has been cleared */
static void window_object_cleared_cb(WebKitWebView *web_view,
WebKitWebFrame *frame,
gpointer context,
gpointer window_object,
gpointer user_data)
{
/* Add classes to JavaScriptCore */
JSClassRef classDef = JSClassCreate(¬ification_def);
JSObjectRef classObj = JSObjectMake(context, classDef, context);
JSObjectRef globalObj = JSContextGetGlobalObject(context);
JSStringRef str = JSStringCreateWithUTF8CString("Notification");
JSObjectSetProperty(context, globalObj, str, classObj, kJSPropertyAttributeNone, NULL);
}
/* Destroy callback */
static void destroy(GtkWidget *widget,
gpointer data )
{
gtk_main_quit();
}
int
main (int argc, char* argv[])
{
/* Initialize the widget set */
gtk_init (&argc, &argv);
/* Create the window widgets */
GtkWidget *main_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
GtkWidget *scrolled_window = gtk_scrolled_window_new (NULL, NULL);
/* Create the WebKit Web View widget */
GtkWidget *web_view = webkit_web_view_new ();
/* Connect the window object cleared event with callback */
g_signal_connect (G_OBJECT (web_view), "window-object-cleared", G_CALLBACK(window_object_cleared_cb), web_view);
/* Place the WebKitWebView in the GtkScrolledWindow */
gtk_container_add (GTK_CONTAINER (scrolled_window), web_view);
gtk_container_add (GTK_CONTAINER (main_window), scrolled_window);
/* Connect the destroy window event with destroy function */
g_signal_connect (G_OBJECT (main_window), "destroy", G_CALLBACK (destroy), NULL);
/* Open webpage */
webkit_web_view_load_uri (WEBKIT_WEB_VIEW (web_view), "file://webkit-03.html");
/* Create the main window */
gtk_window_set_default_size (GTK_WINDOW (main_window), 800, 600);
/* Show the application window */
gtk_widget_show_all (main_window);
/* Enter the main event loop, and wait for user interaction */
gtk_main ();
/* The user lost interest */
return 0;
}
In bold is highlighted the code that declares the class method to make it accessible in JavaScript. Class methods are first implemented using static JSValueRef functions. Then, they are added to an array of class methods, static const JSStaticFunction notification_staticfuncs[], which also defines the actual method names in JavaScript (in the first array argument, the string). Finally, this array of method definitions is passed as argument to the JSClassDefinition.
To compile the program, type:
$ gcc -o webkit-03 webkit-03.c `pkg-config --cflags --libs webkitgtk-3.0 libnotify`
This program loads the following file, webkit-03.html. This HTML code shows a text area and a button. When the button is pressed, the text is send as a desktop notification using the JavaScriptCore extension.
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<h1>Extending JavaScript with WebKit. Notification class.</h1>
<script type="text/javascript">
function send_notification() {
var notification_text = document.getElementById("notification-text").value;
Notification.notify(notification_text);
}
</script>
<form id="form">
<div><textarea id="notification-text" rows="5" cols="40"></textarea></div>
<div><button type="button" onclick="send_notification();">Send notification</button></div>
</form>
</body>
</html>
When the program is run, this is what is displayed in the desktop:
The source code is available at github.com/vrruiz/WebKit-JavaScriptCore-Extensions.
You're a hero! This stuff rock, exactly what I was looking for.
Thank you!
Posted by: Alex | July 26, 2012 at 06:16 PM
wow!!!! i was looking for this since a week.
Posted by: Amrit Virdi | July 31, 2012 at 10:19 AM
For this line:
JSObjectRef classObj = JSObjectMake(context, classDef, context);
I think the third parameter should NULL. Otherwise you could overwrite the context data with the class's private data?
Posted by: Simon | April 23, 2015 at 10:55 PM