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