Calling a JS Function from C++

Let's say that we have a page with the following HTML/JS on it:

<html>
  <head>
    <script>
      function ShowMessage(message)
      {
        document.getElementById('msg').innerHTML = message;
      }
    </script>
  </head>
  <body>
    <div id="msg"></div>
  </body>
</html>

Our goal is to call the JavaScript function ShowMessage() from C++.

Calling the JS Function with EvaluateScript()

The easiest way to call ShowMessage() from C++ is by evaluating a string of JavaScript.

///
/// 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) {
  ///
  /// Call ShowMessage() by evaluating a string of JavaScript
  ///
  view->EvaluateScript("ShowMessage('Howdy!')");
  
}

Calling the JS Function with JSObjectCallAsFunction()

For greater performance and control, you can pass arguments directly to the Function object using JavaScriptCore's JSObjectCallAsFunction().

This API function also offers exception handling, overriding the this object, and more.

πŸ“˜

See the API declaration for JSObjectCallAsFunction() here.

#include <JavaScriptCore/JSRetainPtr.h>

// 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.
  //
  // This call will lock the execution context for the current
  // thread as long as the Ref<> is alive.
  Ref<JSContext> context = caller->LockJSContext();
  
  // Get the underlying JSContextRef for use with the
  // JavaScriptCore C API.
  JSContextRef ctx = context.get();
  
  // Get the ShowMessage function by evaluating a script. We could have
  // also used JSContextGetGlobalObject() and JSObjectGetProperty() to 
  // retrieve this from the global window object as well.
  
  // Create our string of JavaScript, automatically managed by JSRetainPtr
  JSRetainPtr<JSStringRef> str = adopt(
    JSStringCreateWithUTF8CString("ShowMessage"));

    // Evaluate the string "ShowMessage"
  JSValueRef func = JSEvaluateScript(ctx, str.get(), 0, 0, 0, 0);

  // Check if 'func' is actually an Object and not null
  if (JSValueIsObject(ctx, func)) {
    
    // Cast 'func' to an Object, will return null if typecast failed.
    JSObjectRef funcObj = JSValueToObject(ctx, func, 0);

    // Check if 'funcObj' is a Function and not null
    if (funcObj && JSObjectIsFunction(ctx, funcObj)) {
      
      // Create a JS string from null-terminated UTF8 C-string, store it
      // in a smart pointer to release it when it goes out of scope.
      JSRetainPtr<JSStringRef> msg = 
        adopt(JSStringCreateWithUTF8CString("Howdy!"));

      // Create our list of arguments (we only have one)
      const JSValueRef args[] = { JSValueMakeString(ctx, msg.get()) };

      // Count the number of arguments in the array.
      size_t num_args = sizeof(args) / sizeof(JSValueRef*);

      // Create a place to store an exception, if any
      JSValueRef exception = 0;

      // Call the ShowMessage() function with our list of arguments.
      JSValueRef result = JSObjectCallAsFunction(ctx, funcObj, 0, 
                                                 num_args, args, 
                                                 &exception);

      if (exception) {
        // Handle any exceptions thrown from function here.
      }
      
      if (result) {
        // Handle result (if any) here.
      }
    }
  }
}