docs
Loading...
Searching...
No Matches
API

#include <Ultralight/js/API.h>

Overview

A global JavaScript namespace for native functions and data.

A js::API gives page scripts access to your application's functions and data, much like browsers add built-in Web APIs such as console and document on top of core JavaScript.

Once attached to a View, the API provides its global namespace to your pages.

Define an API with a constant and a function, attach it to a View, and load a page:

js::API app("app");
app["version"] = "2.1.0";
app["add"] = [](double a, double b) { return a + b; };
if (app.AttachTo(view.get()))
view->LoadURL("file:///app.html");
A global JavaScript namespace for native functions and data.
Definition API.h:398

Page script accesses the namespace on window:

console.log(app.version); // "2.1.0"
app.add(2, 3); // 5

Binding Functions and Values

The C++ type you bind decides what the page gets:

Binding What the Page Gets
Callable Function
Plain value Read-only constant
Getter and setter Live property
Async callable Function returning a Promise
Bound class Class constructible with new

A dot in a path string (such as "fs.readFile") creates a child namespace object on the page.

Bind nested paths using dot notation and live properties using getters and setters:

app["fs.readFile"] = [](std::string path) { return ReadFile(path); };
app.BindProperty("volume", [] { return g_volume; },
[](double v) { g_volume = v; });

Attaching to a View

Call AttachTo() with a View before loading content. Every page the View loads from then on gets the API automatically, so you don't need to attach again after a navigation.

If you add, replace, or remove bindings after a page is loaded, that page keeps its current bindings until its next navigation. To adjust frame scope or mutability, pass AttachOptions to AttachTo() (see AttachFlags).

Allowed Pages and Origin Rules

By default, only local file:// pages and content loaded with View::LoadHTML() get the API.

Passing origin rules replaces the default policy completely. Only pages matching your rules get the API, so you must include "file://*" if you want local pages to retain access (see ultralight::OriginRules). For decisions origin rules can't express (such as checking a user setting at runtime), set a callback with js::SetInjectionFilter() to allow or withhold the API per page.

Specify origin rules in AttachOptions to allow a local development server alongside local pages:

// A local development server, plus your local pages:
if (app.AttachTo(view.get(),
{ .origin_rules = { "http://localhost:*", "file://*" } }))
view->LoadURL("http://localhost:5173/");

Emitting Events

Native code can push events to attached pages without polling. Events are delivered on the Renderer's thread during a later Renderer::Update().

Call Emit() from any thread with an event name and payload arguments:

app.Emit("saved", std::string("slot1.dat"));

Subscribe on the page using on() or once(), and remove listeners with off():

app.on("saved", (path) => refreshList(path));

Managing API Lifetime

A View keeps an API attached only while the js::API instance exists. Destroying the js::API detaches it from every View. Events stop reaching pages, and any calls to bound functions or class constructors that a page still holds throw a TypeError with code ULJS_DETACHED.

Hold the API as a class member to tie its lifetime to an owner and bind member functions with js::Bind():

class Game {
public:
explicit Game(View* view) {
api_["showMenu"] = js::Bind(this, &Game::ShowMenu);
if (api_.AttachTo(view))
view->LoadURL("file:///app.html");
}
void ShowMenu();
private:
js::API api_{"app"}; // detaches from every View when the Game goes away
};
Web-page container rendered to an offscreen surface.
Definition View.h:483
virtual void LoadURL(const String &url)=0
Load a URL, the View will navigate to it as a new page.
auto Bind(T *instance, M method)
Bind a member function to an instance without writing a lambda.
Definition API.h:1435
Note
Once an API is attached to a View, you must bind and unbind only on the Renderer's thread.
See also
ultralight::OriginRules, js::SetInjectionFilter(), js::BindingGuard, js::ClassBuilder, js::TypeTraits, js::Diagnostics

Classes

class  Binder
 The assignable path that operator[] returns (see Binding Functions and Values in the class overview). More...

Static Public Member Functions

static API Adopt (ULJSAPI api)
 Wrap a handle you own from the C API, taking ownership of it (eg, the result of ulCreateJSAPI()).
static API FromBorrowed (ULJSAPI api)
 Wrap a handle someone else owns, without owning the API (eg, the api member of a js::InjectionRequest).

Public Member Functions

 API (const char *root_path, const APIOptions &options={})
 Create an API rooted at a global namespace path.
 ~API ()
 Destructor (releases this handle).
 API (API &&other) noexcept
 Move constructor (other becomes empty).
API & operator= (API &&other) noexcept
 Move assignment (releases the current handle, and other becomes empty).
 API (const API &)=delete
API & operator= (const API &)=delete
 operator bool () const
 Whether or not this API holds a handle (false if the root path was invalid or reserved, and after a move or LeakRef()).
Binder operator[] (const char *path)
 Get the Binder for a path relative to the root (see Binder).
template<typename Fn, typename... Anns>
requires (detail::Annotation<Anns> && ...)
void Bind (const char *path, Fn &&fn, const Anns &... annotations)
 Bind a callable at a path (the explicit form of api[path] = fn, which also takes annotations).
template<typename T, typename M, typename... Anns>
requires (std::is_member_function_pointer_v<M> && (detail::Annotation<Anns> && ...))
void Bind (const char *path, T *receiver, M method, const Anns &... annotations)
 Bind a member function of an object at a path (the same as binding js::Bind(receiver, method)):
template<typename T>
void SetConstant (const char *path, T &&value)
 Set a constant at a path (the explicit form of api[path] = value).
template<typename T>
ClassBuilder< T > DefineClass (const char *path)
 Define a native class at a path, so page script can call its methods, read its properties, and construct it with new (when you declare a constructor).
template<typename Getter, typename Setter = std::nullptr_t>
void BindProperty (const char *path, Getter &&get, Setter &&set=nullptr)
 Bind a property with a getter and an optional setter.
template<typename... Args>
requires (Marshalable<detail::CapturedArg<Args>> && ...)
void Emit (const char *event_path, Args &&... args)
 Emit an event to the page listeners subscribed with on or once (see Emitting Events in the class overview).
template<typename... Args, typename... Anns>
requires (detail::Annotation<Anns> && ...)
void DefineEvent (const char *event_path, const Anns &... annotations)
 Declare an event and its payload types.
bool Unbind (const char *path)
 Remove the binding at a path, along with its metadata.
std::string schema () const
 Get the API's schema as JSON.
bool DumpSchema (const char *utf8_path)
 Write the JSON from schema() to a file.
void SetMetadata (const char *path, const char *metadata_json)
 Add schema metadata (eg, documentation) to the entry at a path.
void set_diagnostics (Diagnostics level)
 Set the diagnostics level (see Diagnostics).
Diagnostics diagnostics () const
 Get the effective diagnostics level.
bool AttachTo (View *view, const AttachOptions &options={})
 Attach this API to a View.
void DetachFrom (View *view)
 Detach this API from a View.
ULJSAPI raw () const
 Get the underlying handle, for calls into the C API.
ULJSAPI LeakRef ()
 Give up ownership of the underlying handle without releasing it (this API becomes empty).

Constructor & Destructor Documentation

◆ API() [1/3]

API ( const char * root_path,
const APIOptions & options = {} )
inlineexplicit

Create an API rooted at a global namespace path.

Parameters
root_pathThe dot-separated namespace path (eg, "myApp" or "myApp.native"). Every segment must be non-empty. The roots ul and ultralight (and every path under them) are reserved for the library.
optionsThe creation options (see APIOptions).
Note
An invalid or reserved root_path creates an empty API (operator bool returns false) and logs a warning saying why.

◆ ~API()

~API ( )
inline

Destructor (releases this handle).

Note
Destroying the API detaches it from every View (see Managing API Lifetime in the class overview), unless it came from FromBorrowed() or another owning handle to it exists.

◆ API() [2/3]

API ( API && other)
inlinenoexcept

Move constructor (other becomes empty).

◆ API() [3/3]

API ( const API & )
delete

Member Function Documentation

◆ Adopt()

API Adopt ( ULJSAPI api)
inlinestatic

Wrap a handle you own from the C API, taking ownership of it (eg, the result of ulCreateJSAPI()).

Parameters
apiThe handle to take ownership of.
Returns
Returns an API that owns api.
Note
For a handle someone else owns, use FromBorrowed().

◆ AttachTo()

bool AttachTo ( View * view,
const AttachOptions & options = {} )
inlinenodiscard

Attach this API to a View.

The bindings are added to the View's current page (if the origin policy allows it) and to every page the View loads afterwards. The View keeps the API attached until you detach it or destroy this API. Attaching again updates the flags and origin rules.

api.AttachTo(view.get()); // your own content
api.AttachTo(view.get(), { .flags = js::AllFrames }); // and subframes
api.AttachTo(view.get(), { .origin_rules = { "https://*.mygame.com" } }); // the game's site
Parameters
viewThe View to attach to.
optionsThe attach flags and the origin rules for the pages that get the bindings (see AttachOptions).
Returns
Returns true on success, or false if view is nullptr, this API is empty, a flag is unknown, a rule failed to parse, or every owning handle to the API is gone (for an API from FromBorrowed()). Nothing changes then. An unknown flag, a rule that failed to parse, or a destroyed API also logs a warning that says why.
Note
Call this on the Renderer's thread.
Note
APIs that share a namespace prefix (eg, roots myApp.fs and myApp.net) should all be attached before the page loads. An API attached later can't add to a frozen namespace the page already has, so its bindings appear after the page's next navigation.

◆ Bind() [1/2]

template<typename Fn, typename... Anns>
requires (detail::Annotation<Anns> && ...)
void Bind ( const char * path,
Fn && fn,
const Anns &... annotations )
inline

Bind a callable at a path (the explicit form of api[path] = fn, which also takes annotations).

Parameters
pathThe dot-separated path relative to the root (eg, "fs.readFile").
fnThe callable to bind (see Binding Functions and Values in the class overview).
annotationsOptional js::Param names (one per parameter, or none) and at most one js::Doc.
Note
Binding a path that's already bound replaces the old binding and removes the path's metadata (so call SetMetadata() after binding).

◆ Bind() [2/2]

template<typename T, typename M, typename... Anns>
requires (std::is_member_function_pointer_v<M> && (detail::Annotation<Anns> && ...))
void Bind ( const char * path,
T * receiver,
M method,
const Anns &... annotations )
inline

Bind a member function of an object at a path (the same as binding js::Bind(receiver, method)):

api.Bind("save", this, &App::OnSave);
Parameters
pathThe dot-separated path relative to the root (eg, "fs.readFile").
receiverThe object to call method on. It must outlive every call through the binding.
methodThe member function pointer.
annotationsOptional js::Param names (one per parameter, or none) and at most one js::Doc.
Note
To have the binding keep the object alive, bind js::Bind(holder, method) with a shared holder instead.

◆ BindProperty()

template<typename Getter, typename Setter = std::nullptr_t>
void BindProperty ( const char * path,
Getter && get,
Setter && set = nullptr )
inline

Bind a property with a getter and an optional setter.

Unlike a constant, the property is live: every JavaScript read calls get, and every assignment calls set.

api.BindProperty("volume", [] { return g_volume; },
[](double v) { g_volume = v; });
// Page: myApp.volume -> calls the getter
// myApp.volume = 0.5 -> calls the setter
Parameters
pathThe dot-separated path relative to the root.
getThe getter. It takes no parameters and returns a type js::TypeTraits converts, or a js::Result<T> to throw an error into the page.
setThe setter, which takes one parameter of a type js::TypeTraits converts (return a js::Result<void> to reject the assignment with an error). Pass nullptr for a read-only property: assigning to it then throws in strict-mode code and does nothing otherwise.
Note
Assigning a value of the wrong type throws the standard TypeError, and the setter isn't called.

◆ DefineClass()

template<typename T>
ClassBuilder< T > DefineClass ( const char * path)

Define a native class at a path, so page script can call its methods, read its properties, and construct it with new (when you declare a constructor).

api.DefineClass<Database>("Database")
.Constructor<std::string>()
.Method("query", &Database::Query);
Parameters
pathThe dot-separated path relative to the root. The first DefineClass() for a type also takes the JavaScript class name from the last path segment ("db.Database" gives a class called "Database").
Returns
Returns the builder to declare members on. The class is registered when the builder goes out of scope (normally at the end of the statement).
Note
Requires <Ultralight/js/Class.h>. A type binds to one class for the life of the process, so a later DefineClass() for the same type reuses that class and its members (see ClassBuilder).
See also
ClassBuilder

◆ DefineEvent()

template<typename... Args, typename... Anns>
requires (detail::Annotation<Anns> && ...)
void DefineEvent ( const char * event_path,
const Anns &... annotations )
inline

Declare an event and its payload types.

Declared events appear in schema(), and the generated TypeScript declarations give them typed on, once, and off overloads. The declaration only describes the event, so Emit() doesn't check its arguments against the declared types.

api.DefineEvent<std::string>("saved", js::Param("path"), js::Doc("A save finished."));
api.Emit("saved", path); // Page: myApp.on('saved', (path) => ...)
A documentation string for a registration (shown in API::schema()).
Definition API.h:224
A parameter name for a registration (shown in API::schema() and in error messages).
Definition API.h:199
Parameters
event_pathThe event path relative to the root, in the form Emit() takes.
annotationsOptional js::Param names (one per payload type, or none) and at most one js::Doc.
Note
Once an API declares an event, diagnostics (Warn and Strict) warn about every subscription and every Emit() for an event it didn't declare. You should declare every event the API emits.
Note
Declaring an event again replaces its payload types. Declarations are permanent (Unbind() never removes them).
Note
This follows the registration thread rules (see the class overview).

◆ DetachFrom()

void DetachFrom ( View * view)
inline

Detach this API from a View.

Events stop reaching the View's pages right away. The current page keeps the bindings it already has until it navigates (destroying the API instead makes them throw).

Parameters
viewThe View to detach from.
Note
Call this on the Renderer's thread.

◆ diagnostics()

Diagnostics diagnostics ( ) const
inline

Get the effective diagnostics level.

Returns
Returns the level in effect: the UL_JS_DIAGNOSTICS environment override when it applies, else the level set on this API, with Diagnostics::Auto replaced by the Config's JavaScript level (this is never Diagnostics::Auto).

◆ DumpSchema()

bool DumpSchema ( const char * utf8_path)
inline

Write the JSON from schema() to a file.

The library writes the file before this returns. A tool such as gen-typescript.py can then read it.

Parameters
utf8_pathThe file's path as a UTF-8 string (on Windows too). An existing file is replaced.
Returns
Returns whether or not the file was written (false for an empty API, or a NULL or empty utf8_path). A write that fails also logs a warning with the path.
Note
Safe to call from any thread.

◆ Emit()

template<typename... Args>
requires (Marshalable<detail::CapturedArg<Args>> && ...)
void Emit ( const char * event_path,
Args &&... args )
inline

Emit an event to the page listeners subscribed with on or once (see Emitting Events in the class overview).

api.Emit("saved", std::string("slot1.dat"));
// Page: myApp.on('saved', (path) => { ... });
Parameters
event_pathThe event path relative to the root (eg, "saved" or "fs.changed"). A page can subscribe to "fs.changed" on the root with the full path (myApp.on('fs.changed', fn)), or as myApp.fs.on('changed', fn) when something is bound under fs (only then does myApp.fs exist).
argsUp to 16 argument values of types js::TypeTraits converts.
Note
Safe to call from any thread. The arguments are copied here (strings included), so nothing you pass needs to outlive the call.
Note
The event is delivered later, on the Renderer's thread: during a later Renderer::Update(), the listeners run once in each page that had the bindings when you called Emit() (each frame, for an API attached with AllFrames).
See also
dom::Element::dispatchEvent()

◆ FromBorrowed()

API FromBorrowed ( ULJSAPI api)
inlinestatic

Wrap a handle someone else owns, without owning the API (eg, the api member of a js::InjectionRequest).

The result works like any API, but destroying it never detaches the API, and it doesn't keep the API attached once its owners are gone.

Parameters
apiThe borrowed handle.
Returns
Returns an API with its own (non-owning) reference to api. Its raw() is a different handle than api.
Note
For a handle you own, use Adopt().

◆ LeakRef()

ULJSAPI LeakRef ( )
inline

Give up ownership of the underlying handle without releasing it (this API becomes empty).

Returns
Returns the handle. You must call ulDestroyJSAPI() when finished.

◆ operator bool()

operator bool ( ) const
inlineexplicit

Whether or not this API holds a handle (false if the root path was invalid or reserved, and after a move or LeakRef()).

◆ operator=() [1/2]

API & operator= ( API && other)
inlinenoexcept

Move assignment (releases the current handle, and other becomes empty).

◆ operator=() [2/2]

API & operator= ( const API & )
delete

◆ operator[]()

Binder operator[] ( const char * path)
inline

Get the Binder for a path relative to the root (see Binder).

Parameters
pathThe dot-separated path (eg, "greet" or "fs.readFile").
Returns
Returns the Binder for path.

◆ raw()

ULJSAPI raw ( ) const
inline

Get the underlying handle, for calls into the C API.

Returns
Returns the handle (this API keeps ownership).

◆ schema()

std::string schema ( ) const
inline

Get the API's schema as JSON.

The JSON has a format ("ul-schema"), an api ("js"), and a version, then an entry for each binding with its parameter and return types, the named types they use, and the metadata you added (js::Param, js::Doc, and SetMetadata()).

A reader should ignore keys and types it doesn't know. The version changes only for a change that would break an existing reader.

Returns
Returns the JSON text (an empty string for an empty API).
Note
Safe to call from any thread.

◆ set_diagnostics()

void set_diagnostics ( Diagnostics level)
inline

Set the diagnostics level (see Diagnostics).

Most warnings follow the new level right away. Typo warnings (reads of missing properties) start only on pages that get the bindings after you turn them on, but stop right away when you set Diagnostics::Off.

Parameters
levelThe new level (Diagnostics::Auto goes back to the Config's JavaScript level).
Note
You should set the level before attaching the API to a View (or pass it in APIOptions), since attaching at Warn or higher is what also attaches the library's ul.diagnostics API. Raising the level afterward doesn't add it.
Note
The UL_JS_DIAGNOSTICS environment variable overrides this level while developer mode is on (see diagnostics()).
Note
This follows the registration thread rules (see the class overview).

◆ SetConstant()

template<typename T>
void SetConstant ( const char * path,
T && value )
inline

Set a constant at a path (the explicit form of api[path] = value).

The value is copied when you call this, and every page gets that copy. Changing your C++ value later doesn't change the constant (use BindProperty() for a live value). Page script can't modify a constant, and objects and arrays in it are frozen too.

Parameters
pathThe dot-separated path relative to the root.
valueThe value (see the supported types below).
Note
Constants support the JSON-serializable types: numbers, booleans, strings, js::null, std::optional, std::vector, std::map<std::string, T>, and (when ULTRALIGHT_REFLECTION is 1) enums and plain aggregate structs of these. js::undefined has no JSON form, so use js::null instead.
Note
Structs convert field by field, so a custom js::TypeTraits specialization doesn't apply to a constant. Bind a function or property for a custom conversion.
Note
A null const char* binds null (and logs a diagnostics warning). For null on purpose, pass js::null.

◆ SetMetadata()

void SetMetadata ( const char * path,
const char * metadata_json )
inline

Add schema metadata (eg, documentation) to the entry at a path.

schema() merges each fragment over the entry: objects merge field by field, other values replace, and later fragments win.

Binding a path again (replacing its binding) removes that path's metadata, so call this after the Bind() it describes.

api.SetMetadata("add", "{\"doc\": \"Adds two numbers.\"}");
Parameters
pathThe dot-separated path relative to the root, or "" for the document root (its top-level meta, events, and types sections). Root events entries declare events the way DefineEvent() does.
metadata_jsonA JSON object. Anything else is ignored with a logged warning.
Note
This follows the registration thread rules (see the class overview).

◆ Unbind()

bool Unbind ( const char * path)
inline

Remove the binding at a path, along with its metadata.

Pages see the removal at their next navigation. A page that's already loaded (or restored from the back-forward cache) keeps the binding, and functions the page got from it keep working. A js::Task one of them already started finishes normally. The library destroys the bound callable once the last of those functions and Tasks is gone (right away if there's none).

To tie a binding to a C++ object's lifetime instead, see js::BindingGuard.

Parameters
pathThe binding's path relative to the root, as you bound it.
Returns
Returns whether a binding or metadata entry was removed (false for an empty path or a path with neither). You can ignore it, since removing an absent path is harmless.
Note
Event declarations and root metadata (the top-level meta, events, and types sections) are never removed.
Note
This follows the registration thread rules (see the class overview).

The documentation for this class was generated from the following files: