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::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_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)]
43struct PendingStringCallback {
44 id: usize,
45 #[conditional_malloc_size_of]
46 callback: Rc<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_with_cx(
67 Box::new(DataTransferItem::new_inherited(data_store, id)),
68 global,
69 cx,
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("string"),
96 Kind::File { .. } => DOMString::from("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<Rc<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 let pending_callback = PendingStringCallback { id, callback };
122 self.pending_callbacks.borrow_mut().push(pending_callback);
123
124 self.next_callback.set(id + 1);
125 let this = Trusted::new(self);
126
127 self.global()
130 .task_manager()
131 .dom_manipulation_task_source()
132 .queue(task!(invoke_callback: move |cx| {
133 let this = this.root();
134 let maybe_index = this.pending_callbacks.borrow().iter().position(|val| val.id == id);
135 if let Some(index) = maybe_index {
136 let callback = this.pending_callbacks.safe_borrow_mut(cx.no_gc()).swap_remove(index).callback;
137 let _ = callback.Call__(cx, DOMString::from(string), ExceptionHandling::Report);
138 }
139 }));
140 }
141 }
142
143 fn GetAsFile(&self, cx: &mut JSContext) -> Option<DomRoot<File>> {
145 if !self.can_read() {
147 return None;
148 }
149
150 self.item_kind()?.as_file(cx, &self.global())
154 }
155
156 fn WebkitGetAsEntry(&self, cx: &mut JSContext) -> Option<DomRoot<FileSystemEntry>> {
158 if !self.can_read() {
162 return None;
163 }
164 let global = self.global();
165 let file = self.item_kind()?.as_file(cx, &global)?;
168
169 let name = file.Name().to_string();
171
172 let file_entry = FileSystemFileEntry::new(
173 cx,
174 &global,
175 USVString::from(name.clone()),
176 USVString::from(format!("/{}", name)),
177 &file,
178 );
179
180 let root = FileSystemDirectoryEntry::new(
181 cx,
182 &global,
183 USVString::default(),
184 USVString::from(String::from("/")),
185 );
186
187 root.push_child(file_entry.upcast::<FileSystemEntry>());
188
189 let fs = FileSystem::new(
190 cx,
191 &global,
192 USVString::from(String::from("filesystem")),
193 &root,
194 );
195
196 root.set_filesystem(&fs);
197 file_entry.set_filesystem(&fs);
198
199 Some(DomRoot::upcast::<FileSystemEntry>(file_entry))
200 }
201}