script/dom/
datatransferitem.rs1use std::cell::{Cell, Ref, RefCell};
6use std::rc::Rc;
7
8use dom_struct::dom_struct;
9
10use crate::dom::bindings::callback::ExceptionHandling;
11use crate::dom::bindings::cell::DomRefCell;
12use crate::dom::bindings::codegen::Bindings::DataTransferItemBinding::{
13 DataTransferItemMethods, FunctionStringCallback,
14};
15use crate::dom::bindings::refcounted::Trusted;
16use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
17use crate::dom::bindings::root::DomRoot;
18use crate::dom::bindings::str::DOMString;
19use crate::dom::file::File;
20use crate::dom::globalscope::GlobalScope;
21use crate::drag_data_store::{DragDataStore, Kind, Mode};
22use crate::script_runtime::CanGc;
23
24#[dom_struct]
25pub(crate) struct DataTransferItem {
26 reflector_: Reflector,
27 #[ignore_malloc_size_of = "Rc"]
28 #[no_trace]
29 data_store: Rc<RefCell<Option<DragDataStore>>>,
30 id: u16,
31 pending_callbacks: DomRefCell<Vec<PendingStringCallback>>,
32 next_callback: Cell<usize>,
33}
34
35#[derive(JSTraceable, MallocSizeOf)]
36struct PendingStringCallback {
37 id: usize,
38 #[ignore_malloc_size_of = "Rc"]
39 callback: Rc<FunctionStringCallback>,
40}
41
42impl DataTransferItem {
43 fn new_inherited(data_store: Rc<RefCell<Option<DragDataStore>>>, id: u16) -> DataTransferItem {
44 DataTransferItem {
45 reflector_: Reflector::new(),
46 data_store,
47 id,
48 pending_callbacks: Default::default(),
49 next_callback: Cell::new(0),
50 }
51 }
52
53 pub(crate) fn new(
54 global: &GlobalScope,
55 data_store: Rc<RefCell<Option<DragDataStore>>>,
56 id: u16,
57 can_gc: CanGc,
58 ) -> DomRoot<DataTransferItem> {
59 reflect_dom_object(
60 Box::new(DataTransferItem::new_inherited(data_store, id)),
61 global,
62 can_gc,
63 )
64 }
65
66 fn item_kind(&self) -> Option<Ref<'_, Kind>> {
67 Ref::filter_map(self.data_store.borrow(), |data_store| {
68 data_store
69 .as_ref()
70 .and_then(|data_store| data_store.get_by_id(&self.id))
71 })
72 .ok()
73 }
74
75 fn can_read(&self) -> bool {
76 self.data_store
77 .borrow()
78 .as_ref()
79 .is_some_and(|data_store| data_store.mode() != Mode::Protected)
80 }
81}
82
83impl DataTransferItemMethods<crate::DomTypeHolder> for DataTransferItem {
84 fn Kind(&self) -> DOMString {
86 self.item_kind()
87 .map_or(DOMString::new(), |item| match *item {
88 Kind::Text { .. } => DOMString::from("string"),
89 Kind::File { .. } => DOMString::from("file"),
90 })
91 }
92
93 fn Type(&self) -> DOMString {
95 self.item_kind()
96 .map_or(DOMString::new(), |item| item.type_())
97 }
98
99 fn GetAsString(&self, callback: Option<Rc<FunctionStringCallback>>) {
101 let Some(callback) = callback else {
103 return;
104 };
105
106 if !self.can_read() {
108 return;
109 }
110
111 if let Some(string) = self.item_kind().and_then(|item| item.as_string()) {
113 let id = self.next_callback.get();
114 let pending_callback = PendingStringCallback { id, callback };
115 self.pending_callbacks.borrow_mut().push(pending_callback);
116
117 self.next_callback.set(id + 1);
118 let this = Trusted::new(self);
119
120 self.global()
123 .task_manager()
124 .dom_manipulation_task_source()
125 .queue(task!(invoke_callback: move || {
126 let maybe_index = this.root().pending_callbacks.borrow().iter().position(|val| val.id == id);
127 if let Some(index) = maybe_index {
128 let callback = this.root().pending_callbacks.borrow_mut().swap_remove(index).callback;
129 let _ = callback.Call__(DOMString::from(string), ExceptionHandling::Report, CanGc::note());
130 }
131 }));
132 }
133 }
134
135 fn GetAsFile(&self, can_gc: CanGc) -> Option<DomRoot<File>> {
137 if !self.can_read() {
139 return None;
140 }
141
142 self.item_kind()
146 .and_then(|item| item.as_file(&self.global(), can_gc))
147 }
148}