Calling a C Function From JS
Let's say that we have a page with the following HTML/JS on it:
<html>
<head>
</head>
<body>
<button onclick="OnButtonClick();">Click Me</button>
<div id="result"></div>
</body>
</html>
Our goal is to call a C function bound to OnButtonClick()
when a user clicks the button and display a message in <div id="result"></div>
.
Let's dive in.
About JSObjectMakeFunctionWithCallback()
JavaScriptCore provides a method to bind static C callbacks to JavaScript via JSObjectMakeFunctionWithCallback()
.
You can use this method to create JS Function Objects that will call a C function when invoked.
Function Signature
We can only bind static C functions with a certain function signature (typedef'd as JSObjectCallAsFunctionCallback
).
See the API declaration for
JSObjectCallAsFunctionCallback
here for a deeper description of each of the callback parameters.
JSValueRef MyCallback(JSContextRef ctx, JSObjectRef function,
JSObjectRef thisObject, size_t argumentCount,
const JSValueRef arguments[], JSValueRef* exception) {
// Handle JavaScript arguments in our C callback here...
// Optionally return a value back to JavaScript
return JSValueMakeNull(ctx);
}
Using JSObjectMakeFunctionWithCallback()
We will create a function object named OnButtonClick using our C callback and bind it to the global object to expose it to the page.
See the API declaration for
JSObjectMakeFunctionWithCallback()
here.
// This callback will be bound to 'OnButtonClick()' on the page.
JSValueRef OnButtonClick(JSContextRef ctx, JSObjectRef function,
JSObjectRef thisObject, size_t argumentCount,
const JSValueRef arguments[], JSValueRef* exception) {
const char* str =
"document.getElementById('result').innerText = 'Ultralight rocks!'";
// Create our string of JavaScript
JSStringRef script = JSStringCreateWithUTF8CString(str);
// Execute it with JSEvaluateScript, ignoring other parameters for now
JSEvaluateScript(ctx, script, 0, 0, 0, 0);
// Release our string (we only Release what we Create)
JSStringRelease(script);
return JSValueMakeNull(ctx);
}
// Use LoadListener::OnDOMReady to wait for the DOM to load.
void MyApp::OnDOMReady(View* caller,
uint64_t frame_id,
bool is_main_frame,
const String& url) {
// Acquire the JS execution context for the current page.
auto scoped_context = caller->LockJSContext();
// Typecast to the underlying JSContextRef.
JSContextRef ctx = (*scoped_context);
// Create a JavaScript String containing the name of our callback.
JSStringRef name = JSStringCreateWithUTF8CString("OnButtonClick");
// Create a garbage-collected JavaScript function that is bound to our
// native C callback 'OnButtonClick()'.
JSObjectRef func = JSObjectMakeFunctionWithCallback(ctx, name,
OnButtonClick);
// Get the global JavaScript object (aka 'window')
JSObjectRef globalObj = JSContextGetGlobalObject(ctx);
// Store our function in the page's global JavaScript object so that it
// accessible from the page as 'OnButtonClick()'.
JSObjectSetProperty(ctx, globalObj, name, func, 0, 0);
// Release the JavaScript String we created earlier.
JSStringRelease(name);
}
Updated over 1 year ago