Evaluating a String of JS

Using View::EvaluateScript()

The easiest way to evaluate a string of JavaScript in C++ is by using View::EvaluateScript()

///
/// 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) {
  ///
  /// Execute a string of JavaScript
  ///
  view->EvaluateScript("document.body.innerHTML = 'Ultralight rocks!';");
}

Handling Results

View::EvaluateScript() can return the result from a script execution, if any.

The result is returned as a String.

///
/// 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) {
  
  ///
  /// Evaluate a string of JavaScript, store result in 'result'
  ///
  String result = view->EvaluateScript("1 + 1");
  
  ///
  /// result should now contain "2"
  ///
  
}

Using JSEvaluateScript() Directly

View::EvaluateScript() is really just a wrapper for JavaScriptCore's JSEvaluateScript(). This API function offers lower-level control over script execution (exception handling, overriding this object, etc.).

To use this API function we're going to need to create a JavaScript string with JSStringCreateWithUTF8CString() and then pass it to JSEvaluateScript().

///
/// 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);
  
  const char* script = "document.body.innerHTML = 'Ultralight rocks!';";
  
  ///
  /// Create our string of JavaScript
  ///
  JSStringRef str = JSStringCreateWithUTF8CString(script);
  
  ///
  /// Execute it with JSEvaluateScript, ignoring other parameters for now
  ///
  JSEvaluateScript(ctx, str, 0, 0, 0, 0);
  
  ///
  /// Release our string (we only Release what we Create)
  ///
  JSStringRelease(str);
  
}

Managing string lifetime with Retain / Release

The API follows a simple rule-- anything you "Create" you must "Release".

For example, in the above code snippet we called JSStringCreateWithUTF8CString. This API function has the word Create in it so we must release the created object with JSStringRelease() to avoid a memory leak.

If you want to increase the ref-count (to retain the object in a C++ wrapper, for instance), you can call JSStringRetain().

The API provides a JSRetainPtr<> for C++ that automatically manages lifetime for you. Just call adopt() with any of the JSStringCreate methods to create a managed string object.

#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.
  ///
  auto scoped_context = caller->LockJSContext();
  
  ///
  /// Typecast to the underlying JSContextRef.
  ///
  JSContextRef ctx = (*scoped_context);
  
  const char* script = "document.body.innerHTML = 'Ultralight rocks!';";
  
  ///
  /// Create our string of JavaScript, automatically managed by JSRetainPtr
  ///
  JSRetainPtr<JSStringRef> str = adopt(
    JSStringCreateWithUTF8CString(script));
  
  ///
  /// Execute it with JSEvaluateScript, ignoring other parameters for now
  ///
  JSEvaluateScript(ctx, str.get(), 0, 0, 0, 0);
  
}