| /* 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::{Cell, Ref, RefCell}; |
| use std::rc::Rc; |
| |
| use dom_struct::dom_struct; |
| |
| use crate::dom::bindings::callback::ExceptionHandling; |
| use crate::dom::bindings::cell::DomRefCell; |
| use crate::dom::bindings::codegen::Bindings::DataTransferItemBinding::{ |
| DataTransferItemMethods, FunctionStringCallback, |
| }; |
| use crate::dom::bindings::refcounted::Trusted; |
| 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::file::File; |
| use crate::dom::globalscope::GlobalScope; |
| use crate::drag_data_store::{DragDataStore, Kind, Mode}; |
| use crate::script_runtime::CanGc; |
| |
| #[dom_struct] |
| pub(crate) struct DataTransferItem { |
| reflector_: Reflector, |
| #[ignore_malloc_size_of = "Rc"] |
| #[no_trace] |
| data_store: Rc<RefCell<Option<DragDataStore>>>, |
| id: u16, |
| pending_callbacks: DomRefCell<Vec<PendingStringCallback>>, |
| next_callback: Cell<usize>, |
| } |
| |
| #[derive(JSTraceable, MallocSizeOf)] |
| struct PendingStringCallback { |
| id: usize, |
| #[ignore_malloc_size_of = "Rc"] |
| callback: Rc<FunctionStringCallback>, |
| } |
| |
| impl DataTransferItem { |
| fn new_inherited(data_store: Rc<RefCell<Option<DragDataStore>>>, id: u16) -> DataTransferItem { |
| DataTransferItem { |
| reflector_: Reflector::new(), |
| data_store, |
| id, |
| pending_callbacks: Default::default(), |
| next_callback: Cell::new(0), |
| } |
| } |
| |
| pub(crate) fn new( |
| global: &GlobalScope, |
| data_store: Rc<RefCell<Option<DragDataStore>>>, |
| id: u16, |
| can_gc: CanGc, |
| ) -> DomRoot<DataTransferItem> { |
| reflect_dom_object( |
| Box::new(DataTransferItem::new_inherited(data_store, id)), |
| global, |
| can_gc, |
| ) |
| } |
| |
| fn item_kind(&self) -> Option<Ref<'_, Kind>> { |
| Ref::filter_map(self.data_store.borrow(), |data_store| { |
| data_store |
| .as_ref() |
| .and_then(|data_store| data_store.get_by_id(&self.id)) |
| }) |
| .ok() |
| } |
| |
| fn can_read(&self) -> bool { |
| self.data_store |
| .borrow() |
| .as_ref() |
| .is_some_and(|data_store| data_store.mode() != Mode::Protected) |
| } |
| } |
| |
| impl DataTransferItemMethods<crate::DomTypeHolder> for DataTransferItem { |
| /// <https://html.spec.whatwg.org/multipage/#dom-datatransferitem-kind> |
| fn Kind(&self) -> DOMString { |
| self.item_kind() |
| .map_or(DOMString::new(), |item| match *item { |
| Kind::Text { .. } => DOMString::from("string"), |
| Kind::File { .. } => DOMString::from("file"), |
| }) |
| } |
| |
| /// <https://html.spec.whatwg.org/multipage/#dom-datatransferitem-type> |
| fn Type(&self) -> DOMString { |
| self.item_kind() |
| .map_or(DOMString::new(), |item| item.type_()) |
| } |
| |
| /// <https://html.spec.whatwg.org/multipage/#dom-datatransferitem-getasstring> |
| fn GetAsString(&self, callback: Option<Rc<FunctionStringCallback>>) { |
| // Step 1 If the callback is null, return. |
| let Some(callback) = callback else { |
| return; |
| }; |
| |
| // Step 2 If the DataTransferItem object is not in the read/write mode or the read-only mode, return. |
| if !self.can_read() { |
| return; |
| } |
| |
| // Step 3 If the drag data item kind is not text, then return. |
| if let Some(string) = self.item_kind().and_then(|item| item.as_string()) { |
| let id = self.next_callback.get(); |
| let pending_callback = PendingStringCallback { id, callback }; |
| self.pending_callbacks.borrow_mut().push(pending_callback); |
| |
| self.next_callback.set(id + 1); |
| let this = Trusted::new(self); |
| |
| // Step 4 Otherwise, queue a task to invoke callback, |
| // passing the actual data of the item represented by the DataTransferItem object as the argument. |
| self.global() |
| .task_manager() |
| .dom_manipulation_task_source() |
| .queue(task!(invoke_callback: move || { |
| let maybe_index = this.root().pending_callbacks.borrow().iter().position(|val| val.id == id); |
| if let Some(index) = maybe_index { |
| let callback = this.root().pending_callbacks.borrow_mut().swap_remove(index).callback; |
| let _ = callback.Call__(DOMString::from(string), ExceptionHandling::Report, CanGc::note()); |
| } |
| })); |
| } |
| } |
| |
| /// <https://html.spec.whatwg.org/multipage/#dom-datatransferitem-getasfile> |
| fn GetAsFile(&self, can_gc: CanGc) -> Option<DomRoot<File>> { |
| // Step 1 If the DataTransferItem object is not in the read/write mode or the read-only mode, then return null. |
| if !self.can_read() { |
| return None; |
| } |
| |
| // Step 2 If the drag data item kind is not File, then return null. |
| // Step 3 Return a new File object representing the actual data |
| // of the item represented by the DataTransferItem object. |
| self.item_kind() |
| .and_then(|item| item.as_file(&self.global(), can_gc)) |
| } |
| } |