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};
12
13use crate::dom::bindings::callback::{ExceptionHandling, RootedCallback, TracedCallback};
14use crate::dom::bindings::codegen::Bindings::DataTransferItemBinding::{
15 DataTransferItemMethods, FunctionStringCallback,
16};
17use crate::dom::bindings::codegen::Bindings::FileBinding::FileMethods;
18use crate::dom::bindings::inheritance::Castable;
19use crate::dom::bindings::refcounted::Trusted;
20use crate::dom::bindings::reflector::DomGlobal;
21use crate::dom::bindings::root::DomRoot;
22use crate::dom::bindings::str::{DOMString, USVString};
23use crate::dom::file::File;
24use crate::dom::filesystem::FileSystem;
25use crate::dom::filesystemdirectoryentry::FileSystemDirectoryEntry;
26use crate::dom::filesystementry::FileSystemEntry;
27use crate::dom::filesystemfileentry::FileSystemFileEntry;
28use crate::dom::globalscope::GlobalScope;
29use crate::drag::drag_data_store::{DragDataStore, Kind, Mode};
30
31#[dom_struct]
32pub(crate) struct DataTransferItem {
33 reflector_: Reflector,
34 #[conditional_malloc_size_of]
35 #[no_trace]
36 data_store: Rc<RefCell<Option<DragDataStore>>>,
37 id: u16,
38 pending_callbacks: DomRefCell<Vec<PendingStringCallback>>,
39 next_callback: Cell<usize>,
40}
41
42#[derive(JSTraceable, MallocSizeOf)]
43#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
44struct PendingStringCallback {
45 id: usize,
46 callback: TracedCallback<FunctionStringCallback>,
47}
48
49impl DataTransferItem {
50 fn new_inherited(data_store: Rc<RefCell<Option<DragDataStore>>>, id: u16) -> DataTransferItem {
51 DataTransferItem {
52 reflector_: Reflector::new(),
53 data_store,
54 id,
55 pending_callbacks: Default::default(),
56 next_callback: Cell::new(0),
57 }
58 }
59
60 pub(crate) fn new(
61 cx: &mut JSContext,
62 global: &GlobalScope,
63 data_store: Rc<RefCell<Option<DragDataStore>>>,
64 id: u16,
65 ) -> DomRoot<DataTransferItem> {
66 reflect_dom_object(
67 cx,
68 Box::new(DataTransferItem::new_inherited(data_store, id)),
69 global,
70 )
71 }
72
73 fn item_kind(&self) -> Option<Ref<'_, Kind>> {
74 Ref::filter_map(self.data_store.borrow(), |data_store| {
75 data_store
76 .as_ref()
77 .and_then(|data_store| data_store.get_by_id(&self.id))
78 })
79 .ok()
80 }
81
82 fn can_read(&self) -> bool {
83 self.data_store
84 .borrow()
85 .as_ref()
86 .is_some_and(|data_store| data_store.mode() != Mode::Protected)
87 }
88}
89
90impl DataTransferItemMethods<crate::DomTypeHolder> for DataTransferItem {
91 fn Kind(&self) -> DOMString {
93 self.item_kind()
94 .map_or(DOMString::new(), |item| match *item {
95 Kind::Text { .. } => DOMString::from_static("string"),
96 Kind::File { .. } => DOMString::from_static("file"),
97 })
98 }
99
100 fn Type(&self) -> DOMString {
102 self.item_kind()
103 .map_or(DOMString::new(), |item| item.type_())
104 }
105
106 fn GetAsString(&self, callback: Option<RootedCallback<FunctionStringCallback>>) {
108 let Some(callback) = callback else {
110 return;
111 };
112
113 if !self.can_read() {
115 return;
116 }
117
118 if let Some(string) = self.item_kind().and_then(|item| item.as_string()) {
120 let id = self.next_callback.get();
121 self.pending_callbacks
122 .borrow_mut()
123 .push(PendingStringCallback {
124 id,
125 callback: callback.to_traced(),
126 });
127
128 self.next_callback.set(id + 1);
129 let this = Trusted::new(self);
130
131 self.global()
134 .task_manager()
135 .dom_manipulation_task_source()
136 .queue(task!(invoke_callback: move |cx| {
137 let this = this.root();
138 let maybe_index = this.pending_callbacks.borrow().iter().position(|val| val.id == id);
139 if let Some(index) = maybe_index {
140 rooted!(&in(cx) let callback = this
141 .pending_callbacks
142 .safe_borrow_mut(cx.no_gc())
143 .swap_remove(index)
144 .callback);
145 let _ = callback.Call__(cx, DOMString::from(string), ExceptionHandling::Report);
146 }
147 }));
148 }
149 }
150
151 fn GetAsFile(&self, cx: &mut JSContext) -> Option<DomRoot<File>> {
153 if !self.can_read() {
155 return None;
156 }
157
158 self.item_kind()?.as_file(cx, &self.global())
162 }
163
164 fn WebkitGetAsEntry(&self, cx: &mut JSContext) -> Option<DomRoot<FileSystemEntry>> {
166 if !self.can_read() {
170 return None;
171 }
172 let global = self.global();
173 let file = self.item_kind()?.as_file(cx, &global)?;
176
177 let name = file.Name().to_string();
179
180 let file_entry = FileSystemFileEntry::new(
181 cx,
182 &global,
183 USVString::from(name.clone()),
184 USVString::from(format!("/{}", name)),
185 &file,
186 );
187
188 let root = FileSystemDirectoryEntry::new(
189 cx,
190 &global,
191 USVString::default(),
192 USVString::from(String::from("/")),
193 );
194
195 root.push_child(file_entry.upcast::<FileSystemEntry>());
196
197 let fs = FileSystem::new(
198 cx,
199 &global,
200 USVString::from(String::from("filesystem")),
201 &root,
202 );
203
204 root.set_filesystem(&fs);
205 file_entry.set_filesystem(&fs);
206
207 Some(DomRoot::upcast::<FileSystemEntry>(file_entry))
208 }
209}