docs

Calling into the Page

Call page functions, evaluate scripts, and receive typed results directly in C++.

On this page

Your C++ code can evaluate arbitrary strings of JavaScript and call functions defined on a page (with type-converted results).

Example HTML

The examples in this guide use this HTML snippet:

HTML
<html>
  <head>
    <script>
      var score = 42;

      function ShowMessage(message) {
        if (!message) throw new Error("empty message");
        document.getElementById('msg').innerHTML = message;
      }

      function computeTotal(a, b) {
        return a + b;
      }
    </script>
  </head>
  <body>
    <div id="msg"></div>
  </body>
</html>

Getting the Page's Context

Every call into a page goes through its js::Context. You can get it from a View via LoadListener::OnDOMReady():

C++
// Inherited from LoadListener::OnDOMReady:
void MyApp::OnDOMReady(View* caller, uint64_t frame_id, bool is_main_frame,
                       const String& url) {
  if (!is_main_frame)
    return;

  // Get the context of the View's main frame.
  js::Context ctx(caller);
}

πŸ“˜ When to Get the Context

Use OnDOMReady() for calls that need the page's DOM or its scripts. If native code needs to set up state before the page's own scripts run, get the context in OnWindowObjectReady() insteadβ€” it fires before OnDOMReady(). See Handling View Events for details on both callbacks.

One Context per Navigation

Each page gets its own context. After every navigation you'll need to get a new one.

The old context and every value from it are then no longer valid. Anything you do with them fails safely instead of crashing.

🚧 Stay on the Renderer's Thread

Every call into the page must run on the Renderer's thread.

Calling a Function

Index the context with the function's name and call it:

C++
ctx["ShowMessage"]("Howdy!");

Indexing ctx reads a property of the page's global object (ie, window).

Getting Results

Calls to JavaScript functions return js::Result which can be converted to a C++ type. (See Async Callbacks to wait for a function that returns a Promise.)

With a Fallback

Use js::Or() to convert a value or a call's result to a C++ type with a fallback:

C++
double score = js::Or(ctx["score"], 0.0);
double total = js::Or(ctx["computeTotal"](3, 4), 0.0);

You get the fallback if the property is missing, the call fails, or the value isn't already a number (conversion is strict and never coerces).

Type-Checked Call Results

Use Invoke<T>() when you want to call a JavaScript function and enforce a specific C++ return type. It returns a js::Result that holds either the converted value or a js::Error:

C++
js::Result<double> total = ctx["computeTotal"].Invoke<double>(3, 4);
if (total)
  UseTotal(*total);
else if (total.error().is_page_gone())
  return;                              // the page is gone (nothing to report)
else
  Log(total.error().message());        // it threw or didn't return a number

πŸ‘ Ignored Exceptions Are Never Lost

All JavaScript API errors or exceptions encountered are written to Logger when developer mode is active (see Developer Mode and Diagnostics).

Keeping a Function for Later

Store a function in a js::Value to call it later:

C++
js::Value show = ctx["ShowMessage"];

// Later:
if (show.IsCallable())
  show("Howdy again!");

πŸ“˜ The Value of this

Calling a stored function sets this to the global object. Use show.InvokeOn(object, args...) to call it as a method of some object instead.

Running a Script

Evaluating a Script

Call ctx.Evaluate() to run a script on the page:

C++
js::Result<js::Value> result = ctx.Evaluate("score * 2");  // holds 84

The result holds the script's value as a js::Value (or the error if the script threw).

Converting the Result

Use Evaluate<T>() to convert the script's value to a C++ type. Wrap it in js::Or() for a fallback:

C++
double doubled = js::Or(ctx.Evaluate<double>("score * 2"), 0.0);

Evaluating Without a Context

Call View::EvaluateScript() on the View when a string is all you need:

C++
String title = view->EvaluateScript("document.title");

The result is converted to a string (undefined becomes "undefined"). The optional second argument (a String*) receives the message if the script throws.

Handing the Page a Callback

ctx.MakeFunction() creates a JavaScript function that runs your C++ callback:

C++
js::Value cb = ctx.MakeFunction("onTick", [](double dt) { /* ... */ });
ctx["registerTick"](cb);

The page can keep the function and call it whenever it likes:

JavaScript
let tick = null;
function registerTick(cb) { tick = cb; }

// Later: runs the C++ callback with dt = 16.7.
tick(16.7);

Your C++ callback never runs if the page passes an argument of the wrong type (eg, a string for dt). The page gets a TypeError instead.

The callback must be synchronous (it returns a value or a js::Result).

🚧 Callback Lifetime

The callback is destroyed on the Renderer's thread after the page's function is garbage collected. Use a js::API binding instead for a function that must survive navigation or run asynchronously (see Extending JavaScript with Native API).