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