Blog

3 min read

Loading JavaScriptCore extensions dynamically

Jul 6, 2012 10:30:09 AM

When doing a WebKit port many people are looking to extend JavaScriptCore with custom functions which expose features of their particular hardware platform. Usually, they are also looking to load JavaScript extensions dynamically at runtime rather than being forced to recompile the WebKit libraries each time they want to add or change a function.

This blog posting looks at two ways in which WebKit can support dynamically loading JavaScript. JavaScriptCore can also be extended by compiling in extensions and writing IDL files, but that is a subject for a future post.

JavaScriptCore provides a C public interface, which can be found under Source/JavaScriptCore/API. There are JavaDoc style comments describing the individual functions, and some documentation is available on Apple's site. Another blog posting provides a nice, although somewhat OS X specific, overview of the API.

A good strategy when creating your dynamic library is to have a single entry point which will create and return a JavaScript object containing your extension functions and objects. This function should have a signature something like this:

JSObject jsextension_load(JSContextRef ctx);

This function will use the supplied JSContextRef to create a class and make and return an object of that class. The class will contain static functions and possibly values that you want the extension to export and is defined through a JSClassDefinition structure instance.

The class definition is passed in as an argument to a call to JSClassCreate. You only need a single class instance for the lifetime of your  dynamic library. Once the class instance exists, an object can be made using the JSObjectMake call, and this object is the return value of your jsextension_load function.

The next step to to build a dynamic library which exports jsextension_load. Once this is complete, you then need a way of loading the extensions. This could involve specifying the name of the dynamic library to open, or simply scanning a specified directory for dynamic libraries, opening them and seeing if they export a jsextension_load symbol.

This post will look at two ways of loading extensions. The first is by using something similar to the CommonJS "require" function. The second is adding a method to the WebView to load an extension.

WebKit does not support the CommonJS require function, so this will have to be added to the JavaScript interpreter. An easy way of doing this is to add it to the set of static functions associated with the JavaScriptCore global object. These functions are defined in JSGlobalObjectFunctions.h and JSGlobalObjectFunctions.cpp which are found in JavaScriptCore/runtime, and the require function can be added to these files, along with a reference to it in the global object table found in JSGlobalObject.cpp.

The require function can be given a string as an argument and then attempt to dlopen a dynamic library corresponding to that string. The advantage of this approach as opposed to modifying WebView is that the extensions are available to the standalone JavaScript interpreter and the extensions to be loaded are controlled through JavaScript rather than through the WebView.

A second way of loading extensions is to modify the WebView API to support an extension loading function. The problem that occurs with this approach is to ensure you have a valid JavaScript context that can be used when loading the dynamic libraries. The current JavaScript context can be retrieved by calling

toGlobalRef(frame->script()->globalObject(mainThreadNormalWorld())->globalExec()

where frame is the current "main" frame in the WebView.

This is fine, unless you want to load an extension once and have it persist across page loads, as each page load will cause a new JavaScript context to be  created. Fortunately, there is a callback in the FrameLoaderClient which is called after a new global object is created, called dispatchDidClearWindowObject. This callback can be used to trigger the WebView to reload any extension into the new JavaScript context.

The advantage of managing extensions through the WebView rather than through JavaScriptCore is that is minimizes the amount of change to core WebKit code and makes the full interface provided by the WebView available to extensions.

Topics: webkit

Brian Edmond
Written by Brian Edmond

Brian co-founded Crank Software in 2007, after leading the graphics development group at QNX Software Systems where he spent the majority of his career working and consulting on UI design, embedded software architecture, and web browser technology. As he watched embedded graphics teams struggle, he developed an acute understanding of the common workflow problem between the designers and engineers. Brian built Crank Storyboard out of this need to help these teams collaborate more effectively, optimize for exceptional performance, and get their award-winning touch screens to market, close to 50% faster. A triathlon and mountain biking enthusiast (explaining the name Crank Software), Brian is a regular Ironman and cyclocross participant, and can often be found biking the trails with his co-founders behind the Crank offices in Kanata, Ottawa. Brian holds an Electrical Engineering degree from the Technical University of Nova Scotia.

Post a Comment

Featured