Skip to main content

script/dom/datatransfer/
datatransferitemlist.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::cell::RefCell;
6use std::rc::Rc;
7
8use dom_struct::dom_struct;
9use js::rust::MutableHandleValue;
10use script_bindings::reflector::{Reflector, reflect_dom_object};
11
12use crate::dom::bindings::codegen::Bindings::DataTransferItemListBinding::DataTransferItemListMethods;
13use crate::dom::bindings::error::{Error, Fallible};
14use crate::dom::bindings::frozenarray::CachedFrozenArray;
15use crate::dom::bindings::reflector::DomGlobal;
16use crate::dom::bindings::root::DomRoot;
17use crate::dom::bindings::str::DOMString;
18use crate::dom::datatransferitem::DataTransferItem;
19use crate::dom::file::File;
20use crate::dom::window::Window;
21use crate::drag_data_store::{DragDataStore, Kind, Mode};
22use crate::script_runtime::CanGc;
23
24#[dom_struct]
25pub(crate) struct DataTransferItemList {
26    reflector_: Reflector,
27    #[conditional_malloc_size_of]
28    #[no_trace]
29    data_store: Rc<RefCell<Option<DragDataStore>>>,
30    #[ignore_malloc_size_of = "mozjs"]
31    frozen_types: CachedFrozenArray,
32}
33
34impl DataTransferItemList {
35    fn new_inherited(data_store: Rc<RefCell<Option<DragDataStore>>>) -> DataTransferItemList {
36        DataTransferItemList {
37            reflector_: Reflector::new(),
38            frozen_types: CachedFrozenArray::new(),
39            data_store,
40        }
41    }
42
43    pub(crate) fn new(
44        window: &Window,
45        data_store: Rc<RefCell<Option<DragDataStore>>>,
46        can_gc: CanGc,
47    ) -> DomRoot<DataTransferItemList> {
48        reflect_dom_object(
49            Box::new(DataTransferItemList::new_inherited(data_store)),
50            window,
51            can_gc,
52        )
53    }
54
55    pub(crate) fn frozen_types(&self, cx: &mut js::context::JSContext, retval: MutableHandleValue) {
56        self.frozen_types.get_or_init(
57            || {
58                self.data_store
59                    .borrow()
60                    .as_ref()
61                    .map_or(Vec::new(), |data_store| data_store.types())
62            },
63            cx.into(),
64            retval,
65            CanGc::from_cx(cx),
66        );
67    }
68
69    pub(crate) fn invalidate_frozen_types(&self) {
70        self.frozen_types.clear();
71    }
72}
73
74impl DataTransferItemListMethods<crate::DomTypeHolder> for DataTransferItemList {
75    /// <https://html.spec.whatwg.org/multipage/#dom-datatransferitemlist-length>
76    fn Length(&self) -> u32 {
77        // Return zero if the object is in the disabled mode;
78        // otherwise it must return the number of items in the drag data store item list.
79        self.data_store
80            .borrow()
81            .as_ref()
82            .map_or(0, |data_store| data_store.list_len() as u32)
83    }
84
85    /// <https://html.spec.whatwg.org/multipage/#dom-datatransferitemlist-item>
86    fn IndexedGetter(
87        &self,
88        cx: &mut js::context::JSContext,
89        index: u32,
90    ) -> Option<DomRoot<DataTransferItem>> {
91        // Step 1 Return null if it isn't associated with a data store
92        let option = self.data_store.borrow();
93        let data_store = match option.as_ref() {
94            Some(value) => value,
95            _ => return None,
96        };
97
98        // Step 2
99        data_store.get_by_index(index as usize).map(|(id, _)| {
100            DataTransferItem::new(cx, &self.global(), Rc::clone(&self.data_store), *id)
101        })
102    }
103
104    /// <https://html.spec.whatwg.org/multipage/#dom-datatransferitemlist-add>
105    fn Add(
106        &self,
107        cx: &mut js::context::JSContext,
108        data: DOMString,
109        mut type_: DOMString,
110    ) -> Fallible<Option<DomRoot<DataTransferItem>>> {
111        // Step 1 If the DataTransferItemList object is not in the read/write mode, return null.
112        let mut option = self.data_store.borrow_mut();
113        let data_store = match option.as_mut() {
114            Some(value) if value.mode() == Mode::ReadWrite => value,
115            _ => return Ok(None),
116        };
117
118        // Add an item to the drag data store item list whose kind is text,
119        // whose type string is equal to the value of the method's second argument, converted to ASCII lowercase,
120        // and whose data is the string given by the method's first argument.
121        type_.make_ascii_lowercase();
122        data_store.add(Kind::Text { data, type_ }).map(|id| {
123            self.frozen_types.clear();
124
125            // Step 3 Determine the value of the indexed property corresponding to the newly added item,
126            // and return that value (a newly created DataTransferItem object).
127            Some(DataTransferItem::new(
128                cx,
129                &self.global(),
130                Rc::clone(&self.data_store),
131                id,
132            ))
133        })
134    }
135
136    /// <https://html.spec.whatwg.org/multipage/#dom-datatransferitemlist-add>
137    fn Add_(
138        &self,
139        cx: &mut js::context::JSContext,
140        data: &File,
141    ) -> Fallible<Option<DomRoot<DataTransferItem>>> {
142        // Step 1 If the DataTransferItemList object is not in the read/write mode, return null.
143        let mut option = self.data_store.borrow_mut();
144        let data_store = match option.as_mut() {
145            Some(value) if value.mode() == Mode::ReadWrite => value,
146            _ => return Ok(None),
147        };
148
149        // Add an item to the drag data store item list whose kind is File,
150        // whose type string is the type of the File, converted to ASCII lowercase,
151        // and whose data is the same as the File's data.
152        let mut type_ = data.file_type();
153        type_.make_ascii_lowercase();
154        let bytes = data.file_bytes().unwrap_or_default();
155        let name = data.name().clone();
156
157        data_store.add(Kind::File { bytes, name, type_ }).map(|id| {
158            self.frozen_types.clear();
159
160            // Step 3 Determine the value of the indexed property corresponding to the newly added item,
161            // and return that value (a newly created DataTransferItem object).
162            Some(DataTransferItem::new(
163                cx,
164                &self.global(),
165                Rc::clone(&self.data_store),
166                id,
167            ))
168        })
169    }
170
171    /// <https://html.spec.whatwg.org/multipage/#dom-datatransferitemlist-remove>
172    fn Remove(&self, index: u32) -> Fallible<()> {
173        // Step 1 If the DataTransferItemList object is not in the read/write mode,
174        // throw an "InvalidStateError" DOMException.
175        let mut option = self.data_store.borrow_mut();
176        let data_store = match option.as_mut() {
177            Some(value) if value.mode() == Mode::ReadWrite => value,
178            _ => return Err(Error::InvalidState(None)),
179        };
180
181        let index = index as usize;
182
183        // Step 2 If the drag data store does not contain an indexth item, then return.
184        if index < data_store.list_len() {
185            // Step 3 Remove the indexth item from the drag data store.
186            data_store.remove(index);
187            self.frozen_types.clear();
188        }
189
190        Ok(())
191    }
192
193    /// <https://html.spec.whatwg.org/multipage/#dom-datatransferitemlist-clear>
194    fn Clear(&self) {
195        // If the DataTransferItemList object is in the read/write mode, remove all the items from the drag data store.
196        // Otherwise, it must do nothing.
197        let mut option = self.data_store.borrow_mut();
198        let data_store = match option.as_mut() {
199            Some(value) if value.mode() == Mode::ReadWrite => value,
200            _ => return,
201        };
202
203        // If the item list is empty we don't clear it.
204        if data_store.list_len() > 0 {
205            data_store.clear_list();
206            self.frozen_types.clear();
207        }
208    }
209}