Skip to content

niso-xpath

Generated from crates/niso-xpath/CONTRACT.md when the site was built at 7aa23e2.

XPath 1.0 as DOM’s document.evaluate() runs it, over the trees a host lends through niso-host’s Tree, and DOM’s XPath interfaces (XPathEvaluator, XPathExpression, XPathResult) as the host’s platform objects. It does not resolve namespace prefixes (the caller does, as DOM’s XPathNSResolver), and it never changes a tree.

  • Rust:
    • parse, Parsed (prefixes, resolve), Expression (evaluate against a context Node<'t> of a lent Tree, to a Value that lives for that lending), Value, XNode<'t> (a node of the tree, or an element’s attribute by index), string_value, number_to_string, string_to_number, Error, MAX_DEPTH, MAX_NODES.
    • api: register, the three interfaces’ InterfaceInfos, and one function per IDL member, taking the host as Cx: x_path_evaluator::new; x_path_evaluator_base::{create_ns_resolver, xpath_prefixes, xpath_compile}; x_path_expression::evaluate; x_path_result::{get_result_type, get_number_value, get_string_value, get_boolean_value, get_single_node_value, get_invalid_iterator_state, get_snapshot_length, iterate_next, snapshot_item}.
  • C: the generated niso_x_path_* shims (idl/xpath.webidl), unchanged. niso-capi’s entry points (crates/niso-capi/src/xpath.rs) run api through niso_host::capi::call with the document as the host, as every crate’s shim does, until step 6 generates them per crate.
  • IDL, glue: idl/xpath.webidl; the glue’s createExpression and evaluate resolve prefixes through xpathPrefixes and xpathCompile.
  • A tree (Cx::tree), and Attr nodes for attributes a result holds (Cx::attribute_node).
  • The object table: the three interfaces’ objects; a result retains the nodes it holds and names them in held, so the host releases them after its freed.
  • One slot, installed by api::register (a composition installs it: for the document, niso_dom_new): the expression xpath_prefixes parsed, which xpath_compile uses rather than parsing it again. In a host without it, both are NotSupportedError rather than a panic.
  • No tasks, timers, events or script.
  • niso-host: Cx (tree, attribute_node, object, new_object, retain, release, try_with), the tree’s guarantees T1-T12, PlatformObject, Registrar, ExtKey, Error, ErrorCode.

XPath’s data model over the host’s tree: the namespace axis is always empty, as in browsers; doctypes are not in it (neither a child nor a sibling of anything); a CDATA section is a text node; a tree’s topmost node is the root node when it is a document or a fragment, and whatever it is otherwise (a detached element is an element with no parent); an element’s attributes are attribute nodes whose parent is the element, namespace declarations left out of the attribute axis; an element’s Attr given as the context node is that attribute. Name tests follow HTML’s rules for HTML elements in HTML documents: an unprefixed name matches them in the HTML namespace, ASCII case-insensitively, and their attributes likewise. id() looks among the descendants of the context node’s node document, for the first element in document order with each ID (its id attribute). A node-set is in document order. Strings are UTF-8; string-length() and substring() count UTF-16 code units, as browsers do, and a surrogate pair substring() cuts in half becomes U+FFFD.

The DOM interfaces: a result’s type is the one asked for, or for type 0 the value’s own; a node-set as a number, string or boolean is its first node’s string-value; an iterator is invalid once any tree changed after it was made (Tree::version); a result holds the nodes it hands out, attributes as their Attr nodes, until it goes, and an evaluation that fails while making its result releases the nodes it had retained; errors are DOM’s (SyntaxError, NamespaceError, TypeError, NotSupportedError for a type past 9 or a doctype as the context node, InvalidStateError from an invalid iterator).

Checked by tests/eval.rs and tests/api.rs on FakeHost, and through the document by niso-quickjs’s tests and WPT’s domxpath directory.

parse rejects with Error::Syntax an expression nested more than MAX_DEPTH (1000) deep or with more than MAX_NODES (100,000) parts, since evaluating and freeing it recurse as deep as it nests. Tree walks are iterative, so a tree may be as deep as it likes (tests/eval.rs walks a chain 200,000 elements deep).

Not Send: it runs on the agent’s thread, inside one lending of the tree.

An evaluation is one lending of the tree, and walks it linearly: a step merges its context nodes’ results, already in document order, instead of sorting their concatenation, and skips contexts whose results an earlier one’s already hold (tests/eval.rs counts the tree calls). Each read is one indirect call into the host, and names and values are borrowed. On a document of about 200,000 nodes, the document as host evaluates 12 representative expressions about twice as fast (1.6 to 2.3 times across interleaved runs) as when the core implemented the old Tree trait for its DOM (the step’s metrics). A result costs one allocation, plus its node list.

None of its own: tests run on niso-host’s FakeHost and its FakeTree.

XPath over any host’s documents: host.install(niso_xpath::api::register) for the DOM interfaces, or just parse, resolve and cx.tree(|t| expression.evaluate(t, context)).