use std::cell::{Cell, Ref, RefCell};
use std::rc::Rc;
use dom_struct::dom_struct;
use js::context::JSContext;
use script_bindings::cell::DomRefCell;
use script_bindings::reflector::{Reflector, reflect_dom_object_with_cx};
use crate::dom::bindings::callback::ExceptionHandling;
use crate::dom::bindings::codegen::Bindings::DataTransferItemBinding::{
DataTransferItemMethods, FunctionStringCallback,
};
use crate::dom::bindings::codegen::Bindings::FileBinding::FileMethods;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::DomGlobal;
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::{DOMString, USVString};
use crate::dom::file::File;
use crate::dom::filesystem::FileSystem;
use crate::dom::filesystemdirectoryentry::FileSystemDirectoryEntry;
use crate::dom::filesystementry::FileSystemEntry;
use crate::dom::filesystemfileentry::FileSystemFileEntry;
use crate::dom::globalscope::GlobalScope;
use crate::drag_data_store::{DragDataStore, Kind, Mode};
#[dom_struct]
pub(crate) struct DataTransferItem {
reflector_: Reflector,
#[conditional_malloc_size_of]
#[no_trace]
data_store: Rc<RefCell<Option<DragDataStore>>>,
id: u16,
pending_callbacks: DomRefCell<Vec<PendingStringCallback>>,
next_callback: Cell<usize>,
}
#[derive(JSTraceable, MallocSizeOf)]
struct PendingStringCallback {
id: usize,
#[conditional_malloc_size_of]
callback: Rc<FunctionStringCallback>,
}
impl DataTransferItem {
fn new_inherited(data_store: Rc<RefCell<Option<DragDataStore>>>, id: u16) -> DataTransferItem {
DataTransferItem {
reflector_: Reflector::new(),
data_store,
id,
pending_callbacks: Default::default(),
next_callback: Cell::new(0),
}
}
pub(crate) fn new(
cx: &mut JSContext,
global: &GlobalScope,
data_store: Rc<RefCell<Option<DragDataStore>>>,
id: u16,
) -> DomRoot<DataTransferItem> {
reflect_dom_object_with_cx(
Box::new(DataTransferItem::new_inherited(data_store, id)),
global,
cx,
)
}
fn item_kind(&self) -> Option<Ref<'_, Kind>> {
Ref::filter_map(self.data_store.borrow(), |data_store| {
data_store
.as_ref()
.and_then(|data_store| data_store.get_by_id(&self.id))
})
.ok()
}
fn can_read(&self) -> bool {
self.data_store
.borrow()
.as_ref()
.is_some_and(|data_store| data_store.mode() != Mode::Protected)
}
}
impl DataTransferItemMethods<crate::DomTypeHolder> for DataTransferItem {
fn Kind(&self) -> DOMString {
self.item_kind()
.map_or(DOMString::new(), |item| match *item {
Kind::Text { .. } => DOMString::from("string"),
Kind::File { .. } => DOMString::from("file"),
})
}
fn Type(&self) -> DOMString {
self.item_kind()
.map_or(DOMString::new(), |item| item.type_())
}
fn GetAsString(&self, callback: Option<Rc<FunctionStringCallback>>) {
let Some(callback) = callback else {
return;
};
if !self.can_read() {
return;
}
if let Some(string) = self.item_kind().and_then(|item| item.as_string()) {
let id = self.next_callback.get();
let pending_callback = PendingStringCallback { id, callback };
self.pending_callbacks.borrow_mut().push(pending_callback);
self.next_callback.set(id + 1);
let this = Trusted::new(self);
self.global()
.task_manager()
.dom_manipulation_task_source()
.queue(task!(invoke_callback: move |cx| {
let this = this.root();
let maybe_index = this.pending_callbacks.borrow().iter().position(|val| val.id == id);
if let Some(index) = maybe_index {
let callback = this.pending_callbacks.safe_borrow_mut(cx.no_gc()).swap_remove(index).callback;
let _ = callback.Call__(cx, DOMString::from(string), ExceptionHandling::Report);
}
}));
}
}
fn GetAsFile(&self, cx: &mut JSContext) -> Option<DomRoot<File>> {
if !self.can_read() {
return None;
}
self.item_kind()?.as_file(cx, &self.global())
}
fn WebkitGetAsEntry(&self, cx: &mut JSContext) -> Option<DomRoot<FileSystemEntry>> {
if !self.can_read() {
return None;
}
let global = self.global();
let file = self.item_kind()?.as_file(cx, &global)?;
let name = file.Name().to_string();
let file_entry = FileSystemFileEntry::new(
cx,
&global,
USVString::from(name.clone()),
USVString::from(format!("/{}", name)),
&file,
);
let root = FileSystemDirectoryEntry::new(
cx,
&global,
USVString::default(),
USVString::from(String::from("/")),
);
root.push_child(file_entry.upcast::<FileSystemEntry>());
let fs = FileSystem::new(
cx,
&global,
USVString::from(String::from("filesystem")),
&root,
);
root.set_filesystem(&fs);
file_entry.set_filesystem(&fs);
Some(DomRoot::upcast::<FileSystemEntry>(file_entry))
}
}