/* This Source Code Form is subject to the terms of the Mozilla Public * 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/. */ use std::cell::RefCell; use std::rc::Rc; use dom_struct::dom_struct; use js::rust::MutableHandleValue; use crate::dom::bindings::codegen::Bindings::DataTransferItemListBinding::DataTransferItemListMethods; use crate::dom::bindings::error::{Error, Fallible}; use crate::dom::bindings::frozenarray::CachedFrozenArray; use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object}; use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::str::DOMString; use crate::dom::datatransferitem::DataTransferItem; use crate::dom::file::File; use crate::dom::window::Window; use crate::drag_data_store::{DragDataStore, Kind, Mode}; use crate::script_runtime::{CanGc, JSContext}; #[dom_struct] pub(crate) struct DataTransferItemList { reflector_: Reflector, #[ignore_malloc_size_of = "Rc"] #[no_trace] data_store: Rc>>, #[ignore_malloc_size_of = "mozjs"] frozen_types: CachedFrozenArray, } impl DataTransferItemList { fn new_inherited(data_store: Rc>>) -> DataTransferItemList { DataTransferItemList { reflector_: Reflector::new(), frozen_types: CachedFrozenArray::new(), data_store, } } pub(crate) fn new( window: &Window, data_store: Rc>>, can_gc: CanGc, ) -> DomRoot { reflect_dom_object( Box::new(DataTransferItemList::new_inherited(data_store)), window, can_gc, ) } pub(crate) fn frozen_types(&self, cx: JSContext, retval: MutableHandleValue, can_gc: CanGc) { self.frozen_types.get_or_init( || { self.data_store .borrow() .as_ref() .map_or(Vec::new(), |data_store| data_store.types()) }, cx, retval, can_gc, ); } pub(crate) fn invalidate_frozen_types(&self) { self.frozen_types.clear(); } } impl DataTransferItemListMethods for DataTransferItemList { /// fn Length(&self) -> u32 { // Return zero if the object is in the disabled mode; // otherwise it must return the number of items in the drag data store item list. self.data_store .borrow() .as_ref() .map_or(0, |data_store| data_store.list_len() as u32) } /// fn IndexedGetter(&self, index: u32, can_gc: CanGc) -> Option> { // Step 1 Return null if it isn't associated with a data store let option = self.data_store.borrow(); let data_store = match option.as_ref() { Some(value) => value, _ => return None, }; // Step 2 data_store.get_by_index(index as usize).map(|(id, _)| { DataTransferItem::new(&self.global(), Rc::clone(&self.data_store), *id, can_gc) }) } /// fn Add( &self, data: DOMString, mut type_: DOMString, can_gc: CanGc, ) -> Fallible>> { // Step 1 If the DataTransferItemList object is not in the read/write mode, return null. let mut option = self.data_store.borrow_mut(); let data_store = match option.as_mut() { Some(value) if value.mode() == Mode::ReadWrite => value, _ => return Ok(None), }; // Add an item to the drag data store item list whose kind is text, // whose type string is equal to the value of the method's second argument, converted to ASCII lowercase, // and whose data is the string given by the method's first argument. type_.make_ascii_lowercase(); data_store.add(Kind::Text { data, type_ }).map(|id| { self.frozen_types.clear(); // Step 3 Determine the value of the indexed property corresponding to the newly added item, // and return that value (a newly created DataTransferItem object). Some(DataTransferItem::new( &self.global(), Rc::clone(&self.data_store), id, can_gc, )) }) } /// fn Add_(&self, data: &File, can_gc: CanGc) -> Fallible>> { // Step 1 If the DataTransferItemList object is not in the read/write mode, return null. let mut option = self.data_store.borrow_mut(); let data_store = match option.as_mut() { Some(value) if value.mode() == Mode::ReadWrite => value, _ => return Ok(None), }; // Add an item to the drag data store item list whose kind is File, // whose type string is the type of the File, converted to ASCII lowercase, // and whose data is the same as the File's data. let mut type_ = data.file_type(); type_.make_ascii_lowercase(); let bytes = data.file_bytes().unwrap_or_default(); let name = data.name().clone(); data_store.add(Kind::File { bytes, name, type_ }).map(|id| { self.frozen_types.clear(); // Step 3 Determine the value of the indexed property corresponding to the newly added item, // and return that value (a newly created DataTransferItem object). Some(DataTransferItem::new( &self.global(), Rc::clone(&self.data_store), id, can_gc, )) }) } /// fn Remove(&self, index: u32) -> Fallible<()> { // Step 1 If the DataTransferItemList object is not in the read/write mode, // throw an "InvalidStateError" DOMException. let mut option = self.data_store.borrow_mut(); let data_store = match option.as_mut() { Some(value) if value.mode() == Mode::ReadWrite => value, _ => return Err(Error::InvalidState), }; let index = index as usize; // Step 2 If the drag data store does not contain an indexth item, then return. if index < data_store.list_len() { // Step 3 Remove the indexth item from the drag data store. data_store.remove(index); self.frozen_types.clear(); } Ok(()) } /// fn Clear(&self) { // If the DataTransferItemList object is in the read/write mode, remove all the items from the drag data store. // Otherwise, it must do nothing. let mut option = self.data_store.borrow_mut(); let data_store = match option.as_mut() { Some(value) if value.mode() == Mode::ReadWrite => value, _ => return, }; // If the item list is empty we don't clear it. if data_store.list_len() > 0 { data_store.clear_list(); self.frozen_types.clear(); } } }