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::context::JSContext;
10use js::rust::MutableHandleValue;
11use script_bindings::reflector::{Reflector, reflect_dom_object_with_cx};
12
13use crate::dom::bindings::codegen::Bindings::DataTransferItemListBinding::DataTransferItemListMethods;
14use crate::dom::bindings::error::{Error, Fallible};
15use crate::dom::bindings::frozenarray::CachedFrozenArray;
16use crate::dom::bindings::reflector::DomGlobal;
17use crate::dom::bindings::root::DomRoot;
18use crate::dom::bindings::str::DOMString;
19use crate::dom::datatransferitem::DataTransferItem;
20use crate::dom::file::File;
21use crate::dom::window::Window;
22use crate::drag_data_store::{DragDataStore, Kind, Mode};
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        cx: &mut JSContext,
45        window: &Window,
46        data_store: Rc<RefCell<Option<DragDataStore>>>,
47    ) -> DomRoot<DataTransferItemList> {
48        reflect_dom_object_with_cx(
49            Box::new(DataTransferItemList::new_inherited(data_store)),
50            window,
51            cx,
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            cx,
58            || {
59                self.data_store
60                    .borrow()
61                    .as_ref()
62                    .map_or(Vec::new(), |data_store| data_store.types())
63            },
64            retval,
65        );
66    }
67
68    pub(crate) fn invalidate_frozen_types(&self) {
69        self.frozen_types.clear();
70    }
71}
72
73impl DataTransferItemListMethods<crate::DomTypeHolder> for DataTransferItemList {
74    /// <https://html.spec.whatwg.org/multipage/#dom-datatransferitemlist-length>
75    fn Length(&self) -> u32 {
76        // Return zero if the object is in the disabled mode;
77        // otherwise it must return the number of items in the drag data store item list.
78        self.data_store
79            .borrow()
80            .as_ref()
81            .map_or(0, |data_store| data_store.list_len() as u32)
82    }
83
84    /// <https://html.spec.whatwg.org/multipage/#dom-datatransferitemlist-item>
85    fn IndexedGetter(
86        &self,
87        cx: &mut js::context::JSContext,
88        index: u32,
89    ) -> Option<DomRoot<DataTransferItem>> {
90        // Step 1 Return null if it isn't associated with a data store
91        let option = self.data_store.borrow();
92        let data_store = match option.as_ref() {
93            Some(value) => value,
94            _ => return None,
95        };
96
97        // Step 2
98        data_store.get_by_index(index as usize).map(|(id, _)| {
99            DataTransferItem::new(cx, &self.global(), Rc::clone(&self.data_store), *id)
100        })
101    }
102
103    /// <https://html.spec.whatwg.org/multipage/#dom-datatransferitemlist-add>
104    fn Add(
105        &self,
106        cx: &mut js::context::JSContext,
107        data: DOMString,
108        mut type_: DOMString,
109    ) -> Fallible<Option<DomRoot<DataTransferItem>>> {
110        // Step 1 If the DataTransferItemList object is not in the read/write mode, return null.
111        let index = {
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_ })
123        };
124
125        index.map(|id| {
126            self.frozen_types.clear();
127
128            // Step 3 Determine the value of the indexed property corresponding to the newly added item,
129            // and return that value (a newly created DataTransferItem object).
130            Some(DataTransferItem::new(
131                cx,
132                &self.global(),
133                Rc::clone(&self.data_store),
134                id,
135            ))
136        })
137    }
138
139    /// <https://html.spec.whatwg.org/multipage/#dom-datatransferitemlist-add>
140    fn Add_(
141        &self,
142        cx: &mut js::context::JSContext,
143        data: &File,
144    ) -> Fallible<Option<DomRoot<DataTransferItem>>> {
145        let index = {
146            // Step 1 If the DataTransferItemList object is not in the read/write mode, return null.
147            let mut option = self.data_store.borrow_mut();
148            let data_store = match option.as_mut() {
149                Some(value) if value.mode() == Mode::ReadWrite => value,
150                _ => return Ok(None),
151            };
152
153            // Add an item to the drag data store item list whose kind is File,
154            // whose type string is the type of the File, converted to ASCII lowercase,
155            // and whose data is the same as the File's data.
156            let mut type_ = data.file_type();
157            type_.make_ascii_lowercase();
158            let bytes = data.file_bytes().unwrap_or_default();
159            let name = data.name().clone();
160            data_store.add(Kind::File { bytes, name, type_ })
161        };
162        index.map(|id| {
163            self.frozen_types.clear();
164
165            // Step 3 Determine the value of the indexed property corresponding to the newly added item,
166            // and return that value (a newly created DataTransferItem object).
167            Some(DataTransferItem::new(
168                cx,
169                &self.global(),
170                Rc::clone(&self.data_store),
171                id,
172            ))
173        })
174    }
175
176    /// <https://html.spec.whatwg.org/multipage/#dom-datatransferitemlist-remove>
177    fn Remove(&self, index: u32) -> Fallible<()> {
178        // Step 1 If the DataTransferItemList object is not in the read/write mode,
179        // throw an "InvalidStateError" DOMException.
180        let mut option = self.data_store.borrow_mut();
181        let data_store = match option.as_mut() {
182            Some(value) if value.mode() == Mode::ReadWrite => value,
183            _ => return Err(Error::InvalidState(None)),
184        };
185
186        let index = index as usize;
187
188        // Step 2 If the drag data store does not contain an indexth item, then return.
189        if index < data_store.list_len() {
190            // Step 3 Remove the indexth item from the drag data store.
191            data_store.remove(index);
192            self.frozen_types.clear();
193        }
194
195        Ok(())
196    }
197
198    /// <https://html.spec.whatwg.org/multipage/#dom-datatransferitemlist-clear>
199    fn Clear(&self) {
200        // If the DataTransferItemList object is in the read/write mode, remove all the items from the drag data store.
201        // Otherwise, it must do nothing.
202        let mut option = self.data_store.borrow_mut();
203        let data_store = match option.as_mut() {
204            Some(value) if value.mode() == Mode::ReadWrite => value,
205            _ => return,
206        };
207
208        // If the item list is empty we don't clear it.
209        if data_store.list_len() > 0 {
210            data_store.clear_list();
211            self.frozen_types.clear();
212        }
213    }
214}