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 mut option = self.data_store.borrow_mut();
112        let data_store = match option.as_mut() {
113            Some(value) if value.mode() == Mode::ReadWrite => value,
114            _ => return Ok(None),
115        };
116
117        // Add an item to the drag data store item list whose kind is text,
118        // whose type string is equal to the value of the method's second argument, converted to ASCII lowercase,
119        // and whose data is the string given by the method's first argument.
120        type_.make_ascii_lowercase();
121        data_store.add(Kind::Text { data, type_ }).map(|id| {
122            self.frozen_types.clear();
123
124            // Step 3 Determine the value of the indexed property corresponding to the newly added item,
125            // and return that value (a newly created DataTransferItem object).
126            Some(DataTransferItem::new(
127                cx,
128                &self.global(),
129                Rc::clone(&self.data_store),
130                id,
131            ))
132        })
133    }
134
135    /// <https://html.spec.whatwg.org/multipage/#dom-datatransferitemlist-add>
136    fn Add_(
137        &self,
138        cx: &mut js::context::JSContext,
139        data: &File,
140    ) -> Fallible<Option<DomRoot<DataTransferItem>>> {
141        // Step 1 If the DataTransferItemList object is not in the read/write mode, return null.
142        let mut option = self.data_store.borrow_mut();
143        let data_store = match option.as_mut() {
144            Some(value) if value.mode() == Mode::ReadWrite => value,
145            _ => return Ok(None),
146        };
147
148        // Add an item to the drag data store item list whose kind is File,
149        // whose type string is the type of the File, converted to ASCII lowercase,
150        // and whose data is the same as the File's data.
151        let mut type_ = data.file_type();
152        type_.make_ascii_lowercase();
153        let bytes = data.file_bytes().unwrap_or_default();
154        let name = data.name().clone();
155
156        data_store.add(Kind::File { bytes, name, type_ }).map(|id| {
157            self.frozen_types.clear();
158
159            // Step 3 Determine the value of the indexed property corresponding to the newly added item,
160            // and return that value (a newly created DataTransferItem object).
161            Some(DataTransferItem::new(
162                cx,
163                &self.global(),
164                Rc::clone(&self.data_store),
165                id,
166            ))
167        })
168    }
169
170    /// <https://html.spec.whatwg.org/multipage/#dom-datatransferitemlist-remove>
171    fn Remove(&self, index: u32) -> Fallible<()> {
172        // Step 1 If the DataTransferItemList object is not in the read/write mode,
173        // throw an "InvalidStateError" DOMException.
174        let mut option = self.data_store.borrow_mut();
175        let data_store = match option.as_mut() {
176            Some(value) if value.mode() == Mode::ReadWrite => value,
177            _ => return Err(Error::InvalidState(None)),
178        };
179
180        let index = index as usize;
181
182        // Step 2 If the drag data store does not contain an indexth item, then return.
183        if index < data_store.list_len() {
184            // Step 3 Remove the indexth item from the drag data store.
185            data_store.remove(index);
186            self.frozen_types.clear();
187        }
188
189        Ok(())
190    }
191
192    /// <https://html.spec.whatwg.org/multipage/#dom-datatransferitemlist-clear>
193    fn Clear(&self) {
194        // If the DataTransferItemList object is in the read/write mode, remove all the items from the drag data store.
195        // Otherwise, it must do nothing.
196        let mut option = self.data_store.borrow_mut();
197        let data_store = match option.as_mut() {
198            Some(value) if value.mode() == Mode::ReadWrite => value,
199            _ => return,
200        };
201
202        // If the item list is empty we don't clear it.
203        if data_store.list_len() > 0 {
204            data_store.clear_list();
205            self.frozen_types.clear();
206        }
207    }
208}