Calling C++ from JavaScript
This page describes how to call C++ from JavaScript and vice versa.
One of the most powerful features of Molybden is the ability to call C++ from JavaScript and vice versa with automatic types conversion. This feature allows you to setup a two-way communication between the frontend and backend parts of your application.
You can call C++ code from JavaScript to access the file system, execute shell commands, invoke native APIs, work with the hardware devices, do some heavy calculations, etc.
You can also call JavaScript code from C++ to execute some business logic in the frontend part of your application.
Calling JavaScript from C++
You can execute any JavaScript code on the currently loaded web page right from C++. You just need to make sure that the web page is loaded completely and JavaScript is enabled.
To execute JavaScript and get the result back in C++, use the executeJavaScript
method of the Frame
class. The method takes a JavaScript code string as an argument and returns a JsValue
object that represents the result of the execution:
JsValue result = browser->mainFrame()->executeJavaScript("document.title");
The JsValue
class is a wrapper around the JavaScript value. It provides methods to check the value type and convert it to a C++ type. For example:
if (result.isString()) {
std::string title = result.asString();
}
This method in the example above blocks the current thread execution and waits until the given code is executed.
If you would like do not block the current thread execution, then you can use the overloaded method. It takes a callback function as second argument. The callback function will be called asynchronously when the JavaScript code is executed:
browser->mainFrame()->executeJavaScript("document.title", [](JsValue result) {
if (result.isString()) {
std::string title = result.asString();
}
});
Not only you can execute JavaScript code, but also you can work with JavaScript objects and functions from C++. For example, you can get a reference to the window
object and call its methods:
auto window = browser->mainFrame()->executeJavaScript("window").asJsObject();
// Invoke the window.alert("Hello, world!") function.
window->call("alert", "Hello, world!");
// Access the window.location.href property.
auto location = window->getProperty("location").asJsObject();
std::string href = location->getProperty("href").asString();
Calling C++ from JavaScript
You can inject C++ objects and functions into JavaScript and call them from JavaScript code as if they were JavaScript objects and functions. You just need to make sure that the web page is loaded completely and JavaScript is enabled.
To inject a C++ object or function into JavaScript, you need to set it as a property value of a JavaScript object.
Injecting C++ functions
Define a C++ function you would like to call from JavaScript:
std::string greet(std::string name) {
return "Hello " + name + "! This message comes from C++";
}
Then get a reference to the window
JavaScript object, put a property with the greet
name and the function pointer as the property value:
auto window = browser->mainFrame()->executeJavaScript("window").asJsObject();
window->putProperty("greet", &greet);
That’s it! Now you can call the greet
function from JavaScript:
var message = greet("John");
console.log(message);
The code above will print the following message to the Chromium DevTools Console:
Hello John! This message comes from C++
As you can see that the input arguments and the return value are automatically converted between C++ and JavaScript types.
Injecting C++ objects
Define a C++ class with a method you would like to call from JavaScript and mark this method with the JS_ACCESSIBLE_METHOD
macro to make it accessible from JavaScript:
class Greeter : public JsAccessible<Greeter> {
public:
virtual std::string greet(std::string name) {
return "Hello " + name + "! This message comes from C++";
}
private:
JS_ACCESSIBLE_METHOD(greet);
};
Create an instance of the Greeter
class:
auto greeter = std::make_shared<Greeter>();
Then get a reference to the window
JavaScript object, put a property with the greeter
name and the object instance pointer as the property value:
auto window = browser->mainFrame()->executeJavaScript("window").asJsObject();
window->putProperty("greeter", greeter);
That’s it! Now you can call the greet
method of the greeter
object from JavaScript:
var message = greeter.greet("John");
console.log(message);
The code above will print the following message to the Chromium DevTools Console:
Hello John! This message comes from C++
As you can see that the input arguments and the return value are automatically converted between C++ and JavaScript types.
Recommendations
There are several challenges you need to be aware of when you inject C++ objects and functions into JavaScript.
- You need to make sure that the web page is loaded completely and JavaScript is enabled before you inject C++ objects and functions into JavaScript. Otherwise, the injected objects and functions will not be available in JavaScript.
- All injected objects and functions are stored in the memory until the web page is loaded. If you reload the web page, then all injected objects and functions will be destroyed, and you will need to inject them again.
- You might want to inject C++ objects or functions into JavaScript before JavaScript on the web page is executed.
To solve these challenges, you can use the Browser::onInjectJs
delegate that is called when the web page has been loaded, its document element has been created and a custom JavaScript can be executed before any other JavaScript code on the page.
We recommend that you always inject C++ objects and functions into JavaScript from this delegate:
browser->onInjectJs = [](const InjectJsArgs& args, InjectJsAction action) {
args.window->putProperty("greet", greet);
action.proceed();
};
Automatic type conversion
JavaScript and C++ work with different primitive types. Molybden provides automatic type conversion from the JavaScript to C++ types and vice versa.
The following rules are used to convert C++ to JavaScript types:
C++ | JavaScript |
---|---|
double |
Double |
std::string |
String |
bool |
Boolean |
std::shared_ptr<JsArray> |
Array |
std::shared_ptr<JsObject> |
Object |
std::shared_ptr<JsAccessible> |
Object |
The following rules are used to convert JavaScript to C++ types:
JavaScript | C++ |
---|---|
Number |
double |
String |
std::string |
Boolean |
bool |
Array |
std::shared_ptr<JsArray> |
Object |
std::shared_ptr<JsObject> |
If a JavaScript value cannot be converted to a corresponding C++ type, then a JavaScript exception with the error message that describes the problem will be thrown.