1use std::cell::{Ref, RefCell};
6use std::rc::Rc;
7
8use dom_struct::dom_struct;
9use js::rust::{HandleObject, MutableHandleValue};
10use net_traits::image_cache::Image;
11use script_bindings::cell::DomRefCell;
12use script_bindings::match_domstring_ascii;
13use script_bindings::reflector::{Reflector, reflect_dom_object_with_proto};
14
15use crate::dom::bindings::codegen::Bindings::DataTransferBinding::DataTransferMethods;
16use crate::dom::bindings::inheritance::Castable;
17use crate::dom::bindings::reflector::DomGlobal;
18use crate::dom::bindings::root::{Dom, DomRoot};
19use crate::dom::bindings::str::DOMString;
20use crate::dom::datatransferitemlist::DataTransferItemList;
21use crate::dom::element::Element;
22use crate::dom::filelist::FileList;
23use crate::dom::html::htmlimageelement::HTMLImageElement;
24use crate::dom::window::Window;
25use crate::drag_data_store::{DragDataStore, Mode};
26use crate::script_runtime::CanGc;
27
28const VALID_DROP_EFFECTS: [&str; 4] = ["none", "copy", "link", "move"];
29const VALID_EFFECTS_ALLOWED: [&str; 9] = [
30 "none",
31 "copy",
32 "copyLink",
33 "copyMove",
34 "link",
35 "linkMove",
36 "move",
37 "all",
38 "uninitialized",
39];
40
41#[dom_struct]
42pub(crate) struct DataTransfer {
43 reflector_: Reflector,
44 drop_effect: DomRefCell<DOMString>,
45 effect_allowed: DomRefCell<DOMString>,
46 items: Dom<DataTransferItemList>,
47 #[conditional_malloc_size_of]
48 #[no_trace]
49 data_store: Rc<RefCell<Option<DragDataStore>>>,
50}
51
52impl DataTransfer {
53 fn new_inherited(
54 data_store: Rc<RefCell<Option<DragDataStore>>>,
55 item_list: &DataTransferItemList,
56 ) -> DataTransfer {
57 DataTransfer {
58 reflector_: Reflector::new(),
59 drop_effect: DomRefCell::new(DOMString::from("none")),
60 effect_allowed: DomRefCell::new(DOMString::from("none")),
61 items: Dom::from_ref(item_list),
62 data_store,
63 }
64 }
65
66 pub(crate) fn new_with_proto(
67 window: &Window,
68 proto: Option<HandleObject>,
69 can_gc: CanGc,
70 data_store: Rc<RefCell<Option<DragDataStore>>>,
71 ) -> DomRoot<DataTransfer> {
72 let item_list = DataTransferItemList::new(window, Rc::clone(&data_store), can_gc);
73
74 reflect_dom_object_with_proto(
75 Box::new(DataTransfer::new_inherited(data_store, &item_list)),
76 window,
77 proto,
78 can_gc,
79 )
80 }
81
82 pub(crate) fn new(
83 window: &Window,
84 data_store: Rc<RefCell<Option<DragDataStore>>>,
85 can_gc: CanGc,
86 ) -> DomRoot<DataTransfer> {
87 Self::new_with_proto(window, None, can_gc, data_store)
88 }
89
90 pub(crate) fn data_store(&self) -> Option<Ref<'_, DragDataStore>> {
91 Ref::filter_map(self.data_store.borrow(), |data_store| data_store.as_ref()).ok()
92 }
93}
94
95impl DataTransferMethods<crate::DomTypeHolder> for DataTransfer {
96 fn Constructor(
98 window: &Window,
99 proto: Option<HandleObject>,
100 can_gc: CanGc,
101 ) -> DomRoot<DataTransfer> {
102 let mut drag_data_store = DragDataStore::new();
103 drag_data_store.set_mode(Mode::ReadWrite);
104
105 let data_store = Rc::new(RefCell::new(Some(drag_data_store)));
106
107 DataTransfer::new_with_proto(window, proto, can_gc, data_store)
108 }
109
110 fn DropEffect(&self) -> DOMString {
112 self.drop_effect.borrow().clone()
113 }
114
115 fn SetDropEffect(&self, value: DOMString) {
117 if VALID_DROP_EFFECTS.contains(&&*value.str()) {
118 *self.drop_effect.borrow_mut() = value;
119 }
120 }
121
122 fn EffectAllowed(&self) -> DOMString {
124 self.effect_allowed.borrow().clone()
125 }
126
127 fn SetEffectAllowed(&self, value: DOMString) {
129 if self
130 .data_store
131 .borrow()
132 .as_ref()
133 .is_some_and(|data_store| data_store.mode() == Mode::ReadWrite) &&
134 VALID_EFFECTS_ALLOWED.contains(&&*value.str())
135 {
136 *self.drop_effect.borrow_mut() = value;
137 }
138 }
139
140 fn Items(&self) -> DomRoot<DataTransferItemList> {
142 DomRoot::from_ref(&self.items)
143 }
144
145 fn SetDragImage(&self, image: &Element, x: i32, y: i32) {
147 let mut option = self.data_store.borrow_mut();
149 let data_store = match option.as_mut() {
150 Some(value) => value,
151 None => return,
152 };
153
154 if data_store.mode() != Mode::ReadWrite {
156 return;
157 }
158
159 if let Some(image) = image.downcast::<HTMLImageElement>() {
161 match image.image_data().as_ref().and_then(Image::as_raster_image) {
162 Some(image) => data_store.set_bitmap(Some(image), x, y),
163 None => warn!("Vector images are not yet supported in setDragImage"),
164 }
165 }
166 }
167
168 fn Types(&self, cx: &mut js::context::JSContext, retval: MutableHandleValue) {
170 self.items.frozen_types(cx, retval);
171 }
172
173 fn GetData(&self, mut format: DOMString) -> DOMString {
175 let option = self.data_store.borrow();
177 let data_store = match option.as_ref() {
178 Some(value) => value,
179 None => return DOMString::new(),
180 };
181
182 if data_store.mode() == Mode::Protected {
184 return DOMString::new();
185 }
186
187 format.make_ascii_lowercase();
189 let mut convert_to_url = false;
191
192 let type_override = match_domstring_ascii!(format,
193 "text" => Some(DOMString::from("text/plain")),
195 "url" => {
197 convert_to_url = true;
198 Some(DOMString::from("text/uri-list"))
199 },
200 _ => None,
201 );
202 let type_ = type_override.unwrap_or_else(|| format.clone());
204
205 let data = data_store.find_matching_text(&type_);
206
207 if let Some(result) = data {
209 if convert_to_url {
212 }
214
215 result
217 } else {
218 DOMString::new()
221 }
222 }
223
224 fn SetData(&self, format: DOMString, data: DOMString) {
226 let mut option = self.data_store.borrow_mut();
228 let data_store = match option.as_mut() {
229 Some(value) => value,
230 None => return,
231 };
232
233 if data_store.mode() != Mode::ReadWrite {
235 return;
236 }
237
238 data_store.set_data(format, data);
239 self.items.invalidate_frozen_types();
240 }
241
242 fn ClearData(&self, format: Option<DOMString>) {
244 let mut option = self.data_store.borrow_mut();
246 let data_store = match option.as_mut() {
247 Some(value) => value,
248 None => return,
249 };
250
251 if data_store.mode() != Mode::ReadWrite {
253 return;
254 }
255
256 if data_store.clear_data(format) {
257 self.items.invalidate_frozen_types();
258 }
259 }
260
261 fn Files(&self, cx: &mut js::context::JSContext) -> DomRoot<FileList> {
263 let mut files = Vec::new();
265
266 if let Some(data_store) = self.data_store.borrow().as_ref() {
268 data_store.files(cx, &self.global(), &mut files);
269 }
270
271 FileList::new(self.global().as_window(), files, CanGc::from_cx(cx))
273 }
274}