aboutsummaryrefslogtreecommitdiffstats
path: root/components/script_bindings/iterable.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/script_bindings/iterable.rs')
-rw-r--r--components/script_bindings/iterable.rs170
1 files changed, 169 insertions, 1 deletions
diff --git a/components/script_bindings/iterable.rs b/components/script_bindings/iterable.rs
index 61cd4540bae..03efd8a4856 100644
--- a/components/script_bindings/iterable.rs
+++ b/components/script_bindings/iterable.rs
@@ -2,13 +2,36 @@
* 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 `iterable<...>` and `iterable<..., ...>` WebIDL declarations.
+
+use std::cell::Cell;
+use std::marker::PhantomData;
+use std::ptr;
+use std::ptr::NonNull;
+
+use dom_struct::dom_struct;
use js::conversions::ToJSValConvertible;
+use js::jsapi::{Heap, JSObject};
+use js::jsval::UndefinedValue;
+use js::rust::{HandleObject, HandleValue, MutableHandleObject};
+use crate::codegen::GenericBindings::IterableIteratorBinding::{
+ IterableKeyAndValueResult, IterableKeyOrValueResult,
+};
+use crate::conversions::IDLInterface;
+use crate::error::Fallible;
+use crate::interfaces::DomHelpers;
+use crate::realms::InRealm;
+use crate::reflector::{DomGlobalGeneric, DomObjectIteratorWrap, DomObjectWrap, Reflector};
+use crate::root::{Dom, DomRoot, Root};
+use crate::script_runtime::{CanGc, JSContext};
+use crate::trace::{NoTrace, RootedTraceableBox};
use crate::utils::DOMClass;
+use crate::{DomTypes, JSTraceable};
/// The values that an iterator will iterate over.
#[derive(JSTraceable, MallocSizeOf)]
-pub enum IteratorType {
+pub(crate) enum IteratorType {
/// The keys of the iterable object.
Keys,
/// The values of the iterable object.
@@ -38,3 +61,148 @@ pub trait Iterable {
pub trait IteratorDerives {
fn derives(class: &'static DOMClass) -> bool;
}
+
+/// An iterator over the iterable entries of a given DOM interface.
+#[dom_struct]
+pub struct IterableIterator<
+ D: DomTypes,
+ T: DomObjectIteratorWrap<D> + JSTraceable + Iterable + DomGlobalGeneric<D>,
+> {
+ reflector: Reflector,
+ iterable: Dom<T>,
+ type_: IteratorType,
+ index: Cell<u32>,
+ _marker: NoTrace<PhantomData<D>>,
+}
+
+impl<D: DomTypes, T: DomObjectIteratorWrap<D> + JSTraceable + Iterable> IterableIterator<D, T> {
+ pub fn global_(&self, realm: InRealm) -> DomRoot<D::GlobalScope> {
+ <Self as DomGlobalGeneric<D>>::global_(self, realm)
+ }
+}
+
+impl<
+ D: DomTypes,
+ T: DomObjectIteratorWrap<D>
+ + JSTraceable
+ + Iterable
+ + DomGlobalGeneric<D>
+ + IDLInterface
+ + IteratorDerives,
+> IDLInterface for IterableIterator<D, T>
+{
+ fn derives(class: &'static DOMClass) -> bool {
+ <T as IteratorDerives>::derives(class)
+ }
+}
+
+impl<D: DomTypes, T: DomObjectIteratorWrap<D> + JSTraceable + Iterable + DomGlobalGeneric<D>>
+ IterableIterator<D, T>
+{
+ /// Create a new iterator instance for the provided iterable DOM interface.
+ pub(crate) fn new(iterable: &T, type_: IteratorType, realm: InRealm) -> DomRoot<Self> {
+ let iterator = Box::new(IterableIterator {
+ reflector: Reflector::new(),
+ type_,
+ iterable: Dom::from_ref(iterable),
+ index: Cell::new(0),
+ _marker: NoTrace(PhantomData),
+ });
+ <D as DomHelpers<D>>::reflect_dom_object(iterator, &*iterable.global_(realm), CanGc::note())
+ }
+
+ /// Return the next value from the iterable object.
+ #[allow(non_snake_case)]
+ pub fn Next(&self, cx: JSContext) -> Fallible<NonNull<JSObject>> {
+ let index = self.index.get();
+ rooted!(in(*cx) let mut value = UndefinedValue());
+ rooted!(in(*cx) let mut rval = ptr::null_mut::<JSObject>());
+ let result = if index >= self.iterable.get_iterable_length() {
+ dict_return(cx, rval.handle_mut(), true, value.handle())
+ } else {
+ match self.type_ {
+ IteratorType::Keys => {
+ unsafe {
+ self.iterable
+ .get_key_at_index(index)
+ .to_jsval(*cx, value.handle_mut());
+ }
+ dict_return(cx, rval.handle_mut(), false, value.handle())
+ },
+ IteratorType::Values => {
+ unsafe {
+ self.iterable
+ .get_value_at_index(index)
+ .to_jsval(*cx, value.handle_mut());
+ }
+ dict_return(cx, rval.handle_mut(), false, value.handle())
+ },
+ IteratorType::Entries => {
+ rooted!(in(*cx) let mut key = UndefinedValue());
+ unsafe {
+ self.iterable
+ .get_key_at_index(index)
+ .to_jsval(*cx, key.handle_mut());
+ self.iterable
+ .get_value_at_index(index)
+ .to_jsval(*cx, value.handle_mut());
+ }
+ key_and_value_return(cx, rval.handle_mut(), key.handle(), value.handle())
+ },
+ }
+ };
+ self.index.set(index + 1);
+ result.map(|_| NonNull::new(rval.get()).expect("got a null pointer"))
+ }
+}
+
+impl<D: DomTypes, T: DomObjectIteratorWrap<D> + JSTraceable + Iterable + DomGlobalGeneric<D>>
+ DomObjectWrap<D> for IterableIterator<D, T>
+{
+ const WRAP: unsafe fn(
+ JSContext,
+ &D::GlobalScope,
+ Option<HandleObject>,
+ Box<Self>,
+ CanGc,
+ ) -> Root<Dom<Self>> = T::ITER_WRAP;
+}
+
+fn dict_return(
+ cx: JSContext,
+ mut result: MutableHandleObject,
+ done: bool,
+ value: HandleValue,
+) -> Fallible<()> {
+ let mut dict = IterableKeyOrValueResult::empty();
+ dict.done = done;
+ dict.value.set(value.get());
+ rooted!(in(*cx) let mut dict_value = UndefinedValue());
+ unsafe {
+ dict.to_jsval(*cx, dict_value.handle_mut());
+ }
+ result.set(dict_value.to_object());
+ Ok(())
+}
+
+fn key_and_value_return(
+ cx: JSContext,
+ mut result: MutableHandleObject,
+ key: HandleValue,
+ value: HandleValue,
+) -> Fallible<()> {
+ let mut dict = IterableKeyAndValueResult::empty();
+ dict.done = false;
+ dict.value = Some(
+ vec![key, value]
+ .into_iter()
+ .map(|handle| RootedTraceableBox::from_box(Heap::boxed(handle.get())))
+ .collect(),
+ );
+ rooted!(in(*cx) let mut dict_value = UndefinedValue());
+ unsafe {
+ dict.to_jsval(*cx, dict_value.handle_mut());
+ }
+ result.set(dict_value.to_object());
+ Ok(())
+}