1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
//! Runtime-agnostic DOM operations over [`blitz_dom`].
//!
//! `blitz-script` is currently the only path from a language runtime to the
//! DOM, and its operations are entangled with Boa: `JsValue` in, `JsResult`
//! out, a `Context` threaded through, and prototype objects holding the
//! registration. A second runtime cannot reuse any of it. This crate is those
//! operations with the runtime removed, so that a binding does argument
//! coercion and result construction and nothing else.
//!
//! Nothing here depends on Boa, and nothing here may. See `tests/no_boa.rs`.
//!
//! # Shape of the API
//!
//! Every operation is a free function taking the document first, then the node
//! the operation is *on* (what a binding would call `this`), then the
//! arguments in the order the DOM method declares them:
//!
//! ```ignore
//! let child = document::create_element(&mut doc, "div")?;
//! node::append_child(&mut doc, parent, child)?;
//! element::set_attribute(&mut doc, child, "class", "panel")?;
//! ```
//!
//! Readers take `&BaseDocument`, mutators take `&mut BaseDocument`. Every
//! operation returns `Result<T, DomError>`, including the ones that cannot
//! fail today, so that a caller written against this API keeps compiling when
//! one of them grows a failure case.
//!
//! # Borrow discipline
//!
//! **No return value keeps a document borrow alive.** Readers that would
//! naturally hand back `&str` (`text_content`, `inner_html`, `get_attribute`,
//! `css_text`, `get_property_value`) return owned `String` instead. That is a
//! deliberate allocation.
//!
//! The reason is re-entrancy. A binding calls out to guest code between
//! operations, and guest code calls back in; if a borrow were still live
//! across that boundary the next mutation would panic inside `RefCell`, at a
//! call site with no relationship to the code that took the borrow. Paying an
//! allocation on every read makes that class of failure unrepresentable. A
//! borrow taken *inside* an operation may span engine-internal work freely; it
//! just may not outlive the call.
//!
//! # What this crate deliberately does not do
//!
//! - **It does not mark layout dirty and it does not request a redraw.**
//! `blitz-script` routes mutations through `DomCtx::mutate_doc`, which sets
//! a dirty flag and asks the shell for a frame. Both are properties of the
//! embedding, not of the operation, so both stay with the binding. A binding
//! that forgets gets stale geometry reads, which is the exact bug that flag
//! exists to prevent.
//! - **It does not flush layout.** [`geometry::bounding_client_rect`] reads
//! whatever layout currently holds. See that function's documentation.
//! - **It does not upgrade custom elements.** `blitz-script` runs
//! `upgrade_if_defined` after an insertion, which constructs a guest object.
//! That is a runtime operation.
//! - **It has no events.** Event objects, dispatch, listener registration,
//! selection, pointer capture and focus are all out of scope: they need a
//! dispatch model that belongs with the runtime binding. See README.md.
//!
//! # Interning
//!
//! [`atom::Interner`] and [`atom::AtomId`] exist for a guest that cannot
//! cheaply pass strings across its boundary. The binding owns an interner,
//! resolves an incoming `AtomId` to a `&str`, and calls the operation. The
//! resolution happens one level up rather than inside every operation, which
//! is what keeps the signatures here from having to change when a guest with
//! that constraint arrives. See [`atom`] for the ownership rule.
pub use ;
pub use DomError;
pub use Rect;
/// Re-exported so a caller does not have to depend on `blitz-dom` directly
/// just to name the id type an operation takes.
pub use NodeId;
/// Shorthand for the result type every operation returns.
pub type Result<T> = Result;