Module script::dom

source ·
Expand description

The implementation of the DOM.

The DOM is comprised of interfaces (defined by specifications using WebIDL) that are implemented as Rust structs in submodules of this module. Its implementation is documented below.

A DOM object and its reflector

The implementation of an interface Foo in Servo’s DOM involves two related but distinct objects:

  • the DOM object: an instance of the Rust struct dom::foo::Foo (marked with the #[dom_struct] attribute) on the Rust heap;
  • the reflector: a JSObject allocated by SpiderMonkey, that owns the DOM object.

Memory management

Reflectors of DOM objects, and thus the DOM objects themselves, are managed by the SpiderMonkey Garbage Collector. Thus, keeping alive a DOM object is done through its reflector.

For more information, see:

Inheritance

Rust does not support struct inheritance, as would be used for the object-oriented DOM APIs. To work around this issue, Servo stores an instance of the superclass in the first field of its subclasses. (Note that it is stored by value, rather than in a smart pointer such as Dom<T>.)

This implies that a pointer to an object can safely be cast to a pointer to all its classes.

This invariant is enforced by the lint in plugins::lints::inheritance_integrity.

Interfaces which either derive from or are derived by other interfaces implement the Castable trait, which provides three methods is::<T>(), downcast::<T>() and upcast::<T>() to cast across the type hierarchy and check whether a given instance is of a given type.

use dom::bindings::inheritance::Castable;
use dom::element::Element;
use dom::htmlelement::HTMLElement;
use dom::htmlinputelement::HTMLInputElement;

if let Some(elem) = node.downcast::<Element> {
    if elem.is::<HTMLInputElement>() {
        return elem.upcast::<HTMLElement>();
    }
}

Furthermore, when discriminating a given instance against multiple interface types, code generation provides a convenient TypeId enum which can be used to write match expressions instead of multiple calls to Castable::is::<T>. The type_id() method of an instance is provided by the farthest interface it derives from, e.g. EventTarget for HTMLMediaElement. For convenience, that method is also provided on the Node interface to avoid unnecessary upcasts to EventTarget.

use dom::bindings::inheritance::{EventTargetTypeId, NodeTypeId};

match *node.type_id() {
    EventTargetTypeId::Node(NodeTypeId::CharacterData(_)) => ...,
    EventTargetTypeId::Node(NodeTypeId::Element(_)) => ...,
    ...,
}

Construction

DOM objects of type T in Servo have two constructors:

  • a T::new_inherited static method that returns a plain T, and
  • a T::new static method that returns DomRoot<T>.

(The result of either method can be wrapped in Result, if that is appropriate for the type in question.)

The latter calls the former, boxes the result, and creates a reflector corresponding to it by calling dom::bindings::utils::reflect_dom_object (which yields ownership of the object to the SpiderMonkey Garbage Collector). This is the API to use when creating a DOM object.

The former should only be called by the latter, and by subclasses’ new_inherited methods.

DOM object constructors in JavaScript correspond to a T::Constructor static method. This method is always fallible.

Destruction

When the SpiderMonkey Garbage Collector discovers that the reflector of a DOM object is garbage, it calls the reflector’s finalization hook. This function deletes the reflector’s DOM object, calling its destructor in the process.

Mutability and aliasing

Reflectors are JavaScript objects, and as such can be freely aliased. As Rust does not allow mutable aliasing, mutable borrows of DOM objects are not allowed. In particular, any mutable fields use Cell or DomRefCell to manage their mutability.

Reflector and DomObject

Every DOM object has a Reflector as its first (transitive) member field. This contains a *mut JSObject that points to its reflector.

The FooBinding::Wrap function creates the reflector, stores a pointer to the DOM object in the reflector, and initializes the pointer to the reflector in the Reflector field.

The DomObject trait provides a reflector() method that returns the DOM object’s Reflector. It is implemented automatically for DOM structs through the #[dom_struct] attribute.

Implementing methods for a DOM object

  • dom::bindings::codegen::Bindings::FooBindings::FooMethods for methods defined through IDL;
  • &self public methods for public helpers;
  • &self methods for private helpers.

Accessing fields of a DOM object

All fields of DOM objects are private; accessing them from outside their module is done through explicit getter or setter methods.

Inheritance and casting

All DOM interfaces part of an inheritance chain (i.e. interfaces that derive others or are derived from) implement the trait Castable which provides both downcast and upcasts.

fn f(element: &Element) {
    let base = element.upcast::<Node>();
    let derived = element.downcast::<HTMLElement>().unwrap();
}

Adding a new DOM interface

Adding a new interface Foo requires at least the following:

  • adding the new IDL file at components/script/dom/webidls/Foo.webidl;
  • creating components/script/dom/foo.rs;
  • listing foo.rs in components/script/dom/mod.rs;
  • defining the DOM struct Foo with a #[dom_struct] attribute, a superclass or Reflector member, and other members as appropriate;
  • implementing the dom::bindings::codegen::Bindings::FooBindings::FooMethods trait for Foo;
  • adding/updating the match arm in create_element in components/script/dom/create.rs (only applicable to new types inheriting from HTMLElement)

More information is available in the bindings module.

Accessing DOM objects from layout

Layout code can access the DOM through the LayoutDom smart pointer. This does not keep the DOM object alive; we ensure that no DOM code (Garbage Collection in particular) runs while layout is accessing the DOM.

Methods accessible to layout are implemented on LayoutDom<Foo> using LayoutFooHelpers traits.

Re-exports

  • pub use self::webgl_extensions::ext::*;

Modules