docs
Loading...
Searching...
No Matches
JSInterop.h
Go to the documentation of this file.
1///
2/// Copyright (C) 2026 Ultralight, Inc. All rights reserved.
3/// A license is required for commercial use. https://ultralig.ht
4///
5/// @file JSInterop.h
6///
7/// Conversion functions and type traits connecting the DOM and JavaScript APIs.
8///
9/// `#include <Ultralight/dom/JSInterop.h>`
10///
11/// @note This API is a preview and may still change after 2.0.
12///
13/// This header connects the DOM and JavaScript APIs so you can pass DOM elements between C++ and
14/// page script.
15///
16/// Convert elements between C++ and page script using the document's context:
17///
18/// ```
19/// js::Context ctx = dom::GetJSContext(document);
20/// dom::Element hud = document.querySelector("#hud");
21///
22/// // Give page script an element:
23/// ctx.GlobalObject()["hud"] = dom::ToJS(ctx, hud);
24///
25/// // Get the element a script returns:
26/// dom::Element focused = dom::FromJS(
27/// ctx.Evaluate("document.activeElement").value_or(js::Value()));
28/// ```
29///
30/// ## Elements in Bound Functions
31///
32/// Including this header specializes js::TypeTraits for dom::Element, letting bound functions
33/// accept and return elements directly. Passing `null` or `undefined` from JavaScript to a
34/// dom::Element parameter fails with a TypeError, so declare `std::optional<dom::Element>` to
35/// accept them.
36///
37/// Bind functions that take an element and an optional element:
38///
39/// ```
40/// js::API api("app");
41/// api["highlight"] = [](dom::Element el) { el.classList.add("highlight"); };
42/// api["clear"] = [](std::optional<dom::Element> el) {
43/// if (el)
44/// el->classList.remove("highlight");
45/// };
46/// ```
47///
48/// ## Frame Contexts
49///
50/// An element converts to JavaScript only with the context of its own frame. Converting an element
51/// with a context from another frame or View returns an empty js::Value.
52///
53/// How you retrieve a context depends on the document's frame:
54///
55/// - **Main frame documents work with either `js::Context(view)` or dom::GetJSContext().**
56/// - **Subframe documents require dom::GetJSContext().** Passing a View to `js::Context(view)`
57/// wraps only the main frame.
58///
59/// \parblock
60/// @note When JavaScript is disabled or a frame is sandboxed, dom::GetJSContext() returns an empty
61/// js::Context.
62/// \endparblock
63///
64/// \parblock
65/// @note This header is an opt-in include that isn't part of `<Ultralight/DOM.h>` or
66/// `<Ultralight/JS.h>`. When native code converts an element without including it, the
67/// compile error directs you to this file.
68/// \endparblock
69///
70/// @see dom::ToJS(), dom::FromJS(), dom::GetJSContext(), js::TypeTraits, js::Context
71///
72#pragma once
73#include <Ultralight/CAPI/CAPI_DOMDocument.h>
74#include <Ultralight/CAPI/CAPI_DOMElement.h>
78#include <Ultralight/js/Value.h>
79
80namespace ultralight {
81namespace dom {
82
83///
84/// Get an element's JavaScript object (the same object the page's scripts see for it).
85///
86/// Converting one element twice gives the same object (they compare `===`).
87///
88/// @param context The JavaScript context of the element's own frame (see GetJSContext()).
89///
90/// @param element The element to convert.
91///
92/// @return Returns the element's JavaScript object, or an empty Value if `context` belongs to
93/// another frame or View.
94///
95/// @see FromJS()
96///
97inline js::Value ToJS(const js::Context& context, const Element& element) {
98 return js::Value::Adopt(ulDOMElementGetJSValue(element.raw(), context.raw()));
99}
100
101///
102/// Get the element a JavaScript value refers to (the inverse of ToJS()).
103///
104/// The element can be in any frame of the value's View, subframes included. The returned
105/// Element belongs to the element's own page (not the value's), so it stops working when
106/// that page goes away.
107///
108/// @param value A JavaScript value that refers to a DOM element.
109///
110/// @return Returns the element (empty if `value` isn't a DOM element, the element is in
111/// another View, or its document is no longer loaded in a frame).
112///
113/// @note When DOM diagnostics are on, the library logs why it refused an element (see
114/// dom::Element).
115///
116/// @see ToJS()
117///
118inline Element FromJS(const js::Value& value) {
119 return Element::Adopt(ulDOMElementFromJSValue(value.raw()));
120}
121
122///
123/// Get the JavaScript context of a document (the context its page's scripts run in).
124///
125/// This works for the document of any frame. View::GetJSContext() only gives the main frame's
126/// context. Like any js::Context, the result stops working when the document's page goes away.
127///
128/// @param document The document.
129///
130/// @return Returns the context, or an empty Context if the document's frame can't run scripts
131/// (JavaScript is disabled or the document is sandboxed).
132///
133/// @see ToJS()
134///
135inline js::Context GetJSContext(const Document& document) {
136 return js::Context::Adopt(ulDOMDocumentGetJSContext(document.raw()));
137}
138
139} // namespace dom
140
141namespace js {
142
143///
144/// dom::Element as its JavaScript object.
145///
146/// With this specialization, an element can be a bound function's parameter or return value,
147/// an API::Emit() payload, a Resolver result, or an item in a container:
148///
149/// ```
150/// api["highlight"] = [](dom::Element el) { el.classList.add("highlight"); };
151/// api.Emit("picked", picked_element);
152/// ```
153///
154/// Conversions follow ToJS() and FromJS():
155///
156/// - **From JavaScript**, anything FromJS() refuses fails the call with a TypeError. That
157/// includes `null` and `undefined`, so use `std::optional<dom::Element>` to accept them.
158/// - **To JavaScript**, an element becomes its object only in its own frame. It becomes
159/// `undefined` in any other frame or View and once its page is gone. An empty Element
160/// becomes `null`.
161///
162/// @note API::Emit() converts its payload separately for each page that receives the event,
163/// so an element arrives as its object in its own frame and as `undefined` everywhere
164/// else.
165///
166template <>
167struct TypeTraits<dom::Element> {
168 static bool FromJS(ULJSContext, ULJSValue borrowed, dom::Element* out, ULJSValue*) {
169 ULDOMElement element = ulDOMElementFromJSValue(borrowed);
170 if (!element)
171 return false;
172 *out = dom::Element::Adopt(element);
173 return true;
174 }
175 static ULJSValue ToJS(ULJSContext ctx, const dom::Element& value) {
176 if (value.IsEmpty())
177 return ulCreateJSValueNull(ctx);
178 return ulDOMElementGetJSValue(value.raw(), ctx);
179 }
180 static constexpr const char* SchemaType() { return "Element"; }
181};
182
183} // namespace js
184} // namespace ultralight
struct C_JSContext * ULJSContext
Opaque handle to a JavaScript execution context (one page's script world in one View).
Definition View.h:25
The root of a page's DOM tree in a View or frame.
Definition Document.h:106
ULDOMDocument raw() const
Get the C handle, for passing to the <Ultralight/CAPI/CAPI_DOMDocument.h> functions.
Definition Document.h:959
A handle to an element on a page.
Definition Element.h:142
static Element Adopt(ULDOMElement handle)
Wrap a C handle you own, taking ownership of it.
Definition Element.h:1987
ULDOMElement raw() const
Get the C handle, for passing to the <Ultralight/CAPI/CAPI_DOMElement.h> functions.
Definition Element.h:2006
bool IsEmpty() const
Whether or not this Node is empty (it holds no handle).
Definition Node.h:216
JavaScript execution environment for a page.
Definition Context.h:151
ULJSContext raw() const
Get the C API handle without transferring ownership.
Definition Context.h:615
static Context Adopt(ULJSContext handle)
Take ownership of a context handle from the C API.
Definition Context.h:190
A handle to a live JavaScript value.
Definition Value.h:210
static Value Adopt(ULJSValue handle)
Take ownership of a handle from the C API without adding a reference.
Definition Value.h:799
ULJSValue raw() const
Get the C API handle without transferring ownership.
Definition Value.h:820
Direct C++ access to modify page elements and handle events.
Element FromJS(const js::Value &value)
Get the element a JavaScript value refers to (the inverse of ToJS()).
Definition JSInterop.h:118
js::Context GetJSContext(const Document &document)
Get the JavaScript context of a document (the context its page's scripts run in).
Definition JSInterop.h:135
js::Value ToJS(const js::Context &context, const Element &element)
Get an element's JavaScript object (the same object the page's scripts see for it).
Definition JSInterop.h:97
Type-checked JavaScript bridge between C++ and web pages.
Definition JSInterop.h:141
Root namespace for every public Ultralight type, function, and enumeration.
static bool FromJS(ULJSContext, ULJSValue borrowed, dom::Element *out, ULJSValue *)
Definition JSInterop.h:168
static ULJSValue ToJS(ULJSContext ctx, const dom::Element &value)
Definition JSInterop.h:175
static constexpr const char * SchemaType()
Definition JSInterop.h:180
Type conversions between C++ and JavaScript across the bridge.
Definition TypeTraits.h:240