Move more bindings types to script_bindings (#35620)

* Move weak references implementation to script_bindings.

Signed-off-by: Josh Matthews <josh@joshmatthews.net>

* Move maplike/setlike definitions to script_bindings.

Signed-off-by: Josh Matthews <josh@joshmatthews.net>

* Move base error types to script_bindings.

Signed-off-by: Josh Matthews <josh@joshmatthews.net>

* Formatting.

Signed-off-by: Josh Matthews <josh@joshmatthews.net>

---------

Signed-off-by: Josh Matthews <josh@joshmatthews.net>
This commit is contained in:
Josh Matthews 2025-02-23 09:25:46 -05:00 committed by GitHub
parent 0383ba9a5b
commit 381e168877
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 435 additions and 391 deletions

View file

@ -0,0 +1,71 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
/// DOM exceptions that can be thrown by a native DOM method.
#[derive(Clone, Debug, MallocSizeOf)]
pub enum Error {
/// IndexSizeError DOMException
IndexSize,
/// NotFoundError DOMException
NotFound,
/// HierarchyRequestError DOMException
HierarchyRequest,
/// WrongDocumentError DOMException
WrongDocument,
/// InvalidCharacterError DOMException
InvalidCharacter,
/// NotSupportedError DOMException
NotSupported,
/// InUseAttributeError DOMException
InUseAttribute,
/// InvalidStateError DOMException
InvalidState,
/// SyntaxError DOMException
Syntax,
/// NamespaceError DOMException
Namespace,
/// InvalidAccessError DOMException
InvalidAccess,
/// SecurityError DOMException
Security,
/// NetworkError DOMException
Network,
/// AbortError DOMException
Abort,
/// TimeoutError DOMException
Timeout,
/// InvalidNodeTypeError DOMException
InvalidNodeType,
/// DataCloneError DOMException
DataClone,
/// NoModificationAllowedError DOMException
NoModificationAllowed,
/// QuotaExceededError DOMException
QuotaExceeded,
/// TypeMismatchError DOMException
TypeMismatch,
/// InvalidModificationError DOMException
InvalidModification,
/// NotReadableError DOMException
NotReadable,
/// DataError DOMException
Data,
/// OperationError DOMException
Operation,
/// TypeError JavaScript Error
Type(String),
/// RangeError JavaScript Error
Range(String),
/// A JavaScript exception is already pending.
JSFailed,
}
/// The return type for IDL operations that can throw DOM exceptions.
pub type Fallible<T> = Result<T, Error>;
/// The return type for IDL operations that can throw DOM exceptions and
/// return `()`.
pub type ErrorResult = Fallible<()>;

View file

@ -0,0 +1,40 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use js::conversions::ToJSValConvertible;
use crate::utils::DOMClass;
/// The values that an iterator will iterate over.
#[derive(JSTraceable, MallocSizeOf)]
pub enum IteratorType {
/// The keys of the iterable object.
Keys,
/// The values of the iterable object.
Values,
/// The keys and values of the iterable object combined.
Entries,
}
/// A DOM object that can be iterated over using a pair value iterator.
pub trait Iterable {
/// The type of the key of the iterator pair.
type Key: ToJSValConvertible;
/// The type of the value of the iterator pair.
type Value: ToJSValConvertible;
/// Return the number of entries that can be iterated over.
fn get_iterable_length(&self) -> u32;
/// Return the value at the provided index.
fn get_value_at_index(&self, index: u32) -> Self::Value;
/// Return the key at the provided index.
fn get_key_at_index(&self, index: u32) -> Self::Key;
}
/// A version of the [IDLInterface] trait that is specific to types that have
/// iterators defined for them. This allows the `script` crate to define the
/// derives check for the concrete interface type, while the [IteratableIterator]
/// type defined in this module can be parameterized over an unknown generic.
pub trait IteratorDerives {
fn derives(class: &'static DOMClass) -> bool;
}

View file

@ -20,13 +20,17 @@ extern crate malloc_size_of_derive;
pub mod callback;
pub mod constant;
pub mod conversions;
pub mod error;
pub mod inheritance;
pub mod iterable;
pub mod like;
pub mod reflector;
pub mod root;
pub mod script_runtime;
pub mod str;
pub mod trace;
pub mod utils;
pub mod weakref;
#[allow(non_snake_case)]
pub mod codegen {

View file

@ -0,0 +1,111 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
//! Implementation of `setlike<...>` and `maplike<..., ...>` WebIDL declarations.
use js::conversions::ToJSValConvertible;
use crate::iterable::Iterable;
/// Every Setlike dom_struct must implement this to provide access to underlying storage
/// so codegen can automatically generate all setlike methods
///
/// In case you use a type that implements Setlike as underlying storage it's recommended to use `setlike` macro.
// In webidl: `setlike<Key>`
pub trait Setlike {
/// The type of the key of the set.
type Key: ToJSValConvertible + Clone; // clone is for impl<T: Setlike> Maplike for T
fn get_index(&self, index: u32) -> Option<Self::Key>;
fn size(&self) -> u32;
fn add(&self, key: Self::Key);
fn has(&self, key: Self::Key) -> bool;
fn clear(&self);
fn delete(&self, key: Self::Key) -> bool;
}
// we can only have one iterable for T
// so we have impl<T: Maplike> Iterable for T
// and minimal:
impl<T: Setlike> Maplike for T {
type Key = <T as Setlike>::Key;
type Value = <T as Setlike>::Key;
#[inline]
fn get_index(&self, index: u32) -> Option<(Self::Key, Self::Value)> {
self.get_index(index).map(|k| (k.clone(), k))
}
fn get(&self, _key: Self::Key) -> Option<Self::Value> {
unimplemented!()
}
#[inline]
fn size(&self) -> u32 {
self.size()
}
fn set(&self, _key: Self::Key, _value: Self::Value) {
unimplemented!()
}
fn has(&self, _key: Self::Key) -> bool {
unimplemented!()
}
fn clear(&self) {
unimplemented!()
}
fn delete(&self, _key: Self::Key) -> bool {
unimplemented!()
}
}
/// Every Maplike dom_struct must implement this
/// to provide access to underlying storage
/// so codegen can automatically generate all maplike methods
///
/// In case you use a type that implements Maplike as underlying storage it's recommended to use `maplike` macro.
// In webidl: `maplike<Key, Value>`
pub trait Maplike {
/// The type of the key of the map.
type Key: ToJSValConvertible;
/// The type of the value of the map.
type Value: ToJSValConvertible;
fn get_index(&self, index: u32) -> Option<(Self::Key, Self::Value)>;
fn get(&self, key: Self::Key) -> Option<Self::Value>;
fn size(&self) -> u32;
fn set(&self, key: Self::Key, value: Self::Value);
fn has(&self, key: Self::Key) -> bool;
fn clear(&self);
fn delete(&self, key: Self::Key) -> bool;
}
impl<T: Maplike> Iterable for T {
type Key = T::Key;
type Value = T::Value;
#[inline]
fn get_iterable_length(&self) -> u32 {
self.size()
}
#[inline]
fn get_value_at_index(&self, index: u32) -> <T as Maplike>::Value {
// SAFETY: we are checking bounds manually
self.get_index(index).unwrap().1
}
#[inline]
fn get_key_at_index(&self, index: u32) -> <T as Maplike>::Key {
// SAFETY: we are checking bounds manually
self.get_index(index).unwrap().0
}
}

View file

@ -5,6 +5,7 @@
use js::glue::CallObjectTracer;
use js::jsapi::{GCTraceKindToAscii, Heap, JSObject, JSTracer, TraceKind};
use crate::error::Error;
use crate::reflector::Reflector;
use crate::str::{DOMString, USVString};
@ -51,3 +52,4 @@ macro_rules! unsafe_no_jsmanaged_fields(
unsafe_no_jsmanaged_fields!(DOMString);
unsafe_no_jsmanaged_fields!(USVString);
unsafe_no_jsmanaged_fields!(Error);

View file

@ -0,0 +1,165 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
//! Weak-referenceable JS-managed DOM objects.
//!
//! IDL interfaces marked as `weakReferenceable` in `Bindings.conf`
//! automatically implement the `WeakReferenceable` trait in codegen.
//! The instance object is responsible for setting `None` in its own
//! own `WeakBox` when it is collected, through the `DOM_WEAK_SLOT`
//! slot. When all associated `WeakRef` values are dropped, the
//! `WeakBox` itself is dropped too.
use std::cell::Cell;
use std::ops::Drop;
use std::{mem, ptr};
use js::glue::JS_GetReservedSlot;
use js::jsapi::{JSTracer, JS_SetReservedSlot};
use js::jsval::{PrivateValue, UndefinedValue};
use libc::c_void;
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use crate::reflector::DomObject;
use crate::root::DomRoot;
use crate::JSTraceable;
/// The index of the slot wherein a pointer to the weak holder cell is
/// stored for weak-referenceable bindings. We use slot 1 for holding it,
/// this is unsafe for globals, we disallow weak-referenceable globals
/// directly in codegen.
pub const DOM_WEAK_SLOT: u32 = 1;
/// A weak reference to a JS-managed DOM object.
#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_interior)]
pub struct WeakRef<T: WeakReferenceable> {
ptr: ptr::NonNull<WeakBox<T>>,
}
/// The inner box of weak references, public for the finalization in codegen.
#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
pub struct WeakBox<T: WeakReferenceable> {
/// The reference count. When it reaches zero, the `value` field should
/// have already been set to `None`. The pointee contributes one to the count.
pub count: Cell<usize>,
/// The pointer to the JS-managed object, set to None when it is collected.
pub value: Cell<Option<ptr::NonNull<T>>>,
}
/// Trait implemented by weak-referenceable interfaces.
pub trait WeakReferenceable: DomObject + Sized {
/// Downgrade a DOM object reference to a weak one.
fn downgrade(&self) -> WeakRef<Self> {
unsafe {
let object = self.reflector().get_jsobject().get();
let mut slot = UndefinedValue();
JS_GetReservedSlot(object, DOM_WEAK_SLOT, &mut slot);
let mut ptr = slot.to_private() as *mut WeakBox<Self>;
if ptr.is_null() {
trace!("Creating new WeakBox holder for {:p}.", self);
ptr = Box::into_raw(Box::new(WeakBox {
count: Cell::new(1),
value: Cell::new(Some(ptr::NonNull::from(self))),
}));
let val = PrivateValue(ptr as *const c_void);
JS_SetReservedSlot(object, DOM_WEAK_SLOT, &val);
}
let box_ = &*ptr;
assert!(box_.value.get().is_some());
let new_count = box_.count.get() + 1;
trace!(
"Incrementing WeakBox refcount for {:p} to {}.",
self,
new_count
);
box_.count.set(new_count);
WeakRef {
ptr: ptr::NonNull::new_unchecked(ptr),
}
}
}
}
impl<T: WeakReferenceable> WeakRef<T> {
/// Create a new weak reference from a `WeakReferenceable` interface instance.
/// This is just a convenience wrapper around `<T as WeakReferenceable>::downgrade`
/// to not have to import `WeakReferenceable`.
pub fn new(value: &T) -> Self {
value.downgrade()
}
/// DomRoot a weak reference. Returns `None` if the object was already collected.
pub fn root(&self) -> Option<DomRoot<T>> {
unsafe { &*self.ptr.as_ptr() }
.value
.get()
.map(|ptr| unsafe { DomRoot::from_ref(&*ptr.as_ptr()) })
}
/// Return whether the weakly-referenced object is still alive.
pub fn is_alive(&self) -> bool {
unsafe { &*self.ptr.as_ptr() }.value.get().is_some()
}
}
impl<T: WeakReferenceable> Clone for WeakRef<T> {
fn clone(&self) -> WeakRef<T> {
unsafe {
let box_ = &*self.ptr.as_ptr();
let new_count = box_.count.get() + 1;
box_.count.set(new_count);
WeakRef { ptr: self.ptr }
}
}
}
impl<T: WeakReferenceable> MallocSizeOf for WeakRef<T> {
fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
0
}
}
impl<T: WeakReferenceable> PartialEq for WeakRef<T> {
fn eq(&self, other: &Self) -> bool {
unsafe {
(*self.ptr.as_ptr()).value.get().map(ptr::NonNull::as_ptr) ==
(*other.ptr.as_ptr()).value.get().map(ptr::NonNull::as_ptr)
}
}
}
impl<T: WeakReferenceable> PartialEq<T> for WeakRef<T> {
fn eq(&self, other: &T) -> bool {
unsafe {
match self.ptr.as_ref().value.get() {
Some(ptr) => ptr::eq(ptr.as_ptr(), other),
None => false,
}
}
}
}
unsafe impl<T: WeakReferenceable> JSTraceable for WeakRef<T> {
unsafe fn trace(&self, _: *mut JSTracer) {
// Do nothing.
}
}
impl<T: WeakReferenceable> Drop for WeakRef<T> {
fn drop(&mut self) {
unsafe {
let (count, value) = {
let weak_box = &*self.ptr.as_ptr();
assert!(weak_box.count.get() > 0);
let count = weak_box.count.get() - 1;
weak_box.count.set(count);
(count, weak_box.value.get())
};
if count == 0 {
assert!(value.is_none());
mem::drop(Box::from_raw(self.ptr.as_ptr()));
}
}
}
}