aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/drag_data_store.rs
diff options
context:
space:
mode:
authorGae24 <96017547+Gae24@users.noreply.github.com>2024-11-25 11:49:04 +0100
committerGitHub <noreply@github.com>2024-11-25 10:49:04 +0000
commitc9e3d3e25e37068cff5164d83dfa906a7d74f528 (patch)
tree0c7dd286f6437ca17a98d74a68b4a0c821ca46c5 /components/script/drag_data_store.rs
parent810a91ecac71f656ca6573045ff51e7fee35ca53 (diff)
downloadservo-c9e3d3e25e37068cff5164d83dfa906a7d74f528.tar.gz
servo-c9e3d3e25e37068cff5164d83dfa906a7d74f528.zip
Implement `DataTransfer` and related interfaces (#34205)
* add datatransfer interfaces Signed-off-by: Gae24 <96017547+Gae24@users.noreply.github.com> * implement DataTransfer interface implemented Constructor, setter and getter for drop_effect and effect_allowed, Items and SetDragImage Signed-off-by: Gae24 <96017547+Gae24@users.noreply.github.com> * implement DataTransferItem interface Implemented Kind, Type, GetAsString, GetAsFile. Marked DataTransfer as weakReferenceable to access its field inside DataTransferItemList and DataTransferItem. Signed-off-by: Gae24 <96017547+Gae24@users.noreply.github.com> * implement DataTransferItemList interface implemented Lenght, Getter, Add, Remove, Clear Signed-off-by: Gae24 <96017547+Gae24@users.noreply.github.com> * added DataTransfer's old interface Signed-off-by: Gae24 <96017547+Gae24@users.noreply.github.com> * DataTransfer: implemented GetData, SetData, SetData Signed-off-by: Gae24 <96017547+Gae24@users.noreply.github.com> * set Weakref to DataTransfer in DataTransferItemList and DataTransferItem Signed-off-by: Gae24 <96017547+Gae24@users.noreply.github.com> * DataTransfer: implemented Types and Files Signed-off-by: Gae24 <96017547+Gae24@users.noreply.github.com> * cleanup get_data, set_data, clear_data Signed-off-by: Gae24 <96017547+Gae24@users.noreply.github.com> * fix clippy warning Signed-off-by: Gae24 <96017547+Gae24@users.noreply.github.com> * add drag data store Signed-off-by: Gae24 <96017547+Gae24@users.noreply.github.com> * fix DataTransfer's Types() behaviour Signed-off-by: Gae24 <96017547+Gae24@users.noreply.github.com> * DataTransferItem: use the underlying drag data store Signed-off-by: Gae24 <96017547+Gae24@users.noreply.github.com> * fix DataTransferItemList's getter and remove Signed-off-by: Gae24 <96017547+Gae24@users.noreply.github.com> * fix clippy warnings Signed-off-by: Gae24 <96017547+Gae24@users.noreply.github.com> * update test expectations Signed-off-by: Gae24 <96017547+Gae24@users.noreply.github.com> --------- Signed-off-by: Gae24 <96017547+Gae24@users.noreply.github.com>
Diffstat (limited to 'components/script/drag_data_store.rs')
-rw-r--r--components/script/drag_data_store.rs282
1 files changed, 282 insertions, 0 deletions
diff --git a/components/script/drag_data_store.rs b/components/script/drag_data_store.rs
new file mode 100644
index 00000000000..8cc893dd73c
--- /dev/null
+++ b/components/script/drag_data_store.rs
@@ -0,0 +1,282 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+use std::sync::Arc;
+
+use pixels::Image;
+use script_traits::serializable::BlobImpl;
+
+use crate::dom::bindings::error::{Error, Fallible};
+use crate::dom::bindings::root::DomRoot;
+use crate::dom::bindings::str::DOMString;
+use crate::dom::file::File;
+use crate::dom::globalscope::GlobalScope;
+use crate::script_runtime::CanGc;
+
+/// <https://html.spec.whatwg.org/multipage/#the-drag-data-item-kind>
+#[derive(Clone)]
+pub enum Kind {
+ Text(PlainString),
+ File(Binary),
+}
+
+#[derive(Clone)]
+pub struct PlainString {
+ data: DOMString,
+ type_: DOMString,
+}
+
+impl PlainString {
+ pub fn new(data: DOMString, type_: DOMString) -> Self {
+ Self { data, type_ }
+ }
+}
+
+#[derive(Clone)]
+pub struct Binary {
+ bytes: Vec<u8>,
+ name: DOMString,
+ type_: String,
+}
+
+impl Binary {
+ pub fn new(bytes: Vec<u8>, name: DOMString, type_: String) -> Self {
+ Self { bytes, name, type_ }
+ }
+}
+
+impl Kind {
+ pub fn type_(&self) -> DOMString {
+ match self {
+ Kind::Text(string) => string.type_.clone(),
+ Kind::File(binary) => DOMString::from(binary.type_.clone()),
+ }
+ }
+
+ pub fn as_string(&self) -> Option<DOMString> {
+ match self {
+ Kind::Text(string) => Some(string.data.clone()),
+ Kind::File(_) => None,
+ }
+ }
+
+ // TODO for now we create a new BlobImpl
+ // since File constructor requires moving it.
+ pub fn as_file(&self, global: &GlobalScope) -> Option<DomRoot<File>> {
+ match self {
+ Kind::Text(_) => None,
+ Kind::File(binary) => Some(File::new(
+ global,
+ BlobImpl::new_from_bytes(binary.bytes.clone(), binary.type_.clone()),
+ binary.name.clone(),
+ None,
+ CanGc::note(),
+ )),
+ }
+ }
+
+ fn text_type_matches(&self, type_: &str) -> bool {
+ matches!(self, Kind::Text(string) if string.type_.eq(type_))
+ }
+
+ fn is_file(&self) -> bool {
+ matches!(self, Kind::File(_))
+ }
+}
+
+/// <https://html.spec.whatwg.org/multipage/#drag-data-store-bitmap>
+#[allow(dead_code)] // TODO this used by DragEvent.
+struct Bitmap {
+ image: Option<Arc<Image>>,
+ x: i32,
+ y: i32,
+}
+
+/// Control the behaviour of the drag data store
+#[derive(Clone, Copy, Eq, PartialEq)]
+pub enum Mode {
+ /// <https://html.spec.whatwg.org/multipage/#concept-dnd-rw>
+ ReadWrite,
+ /// <https://html.spec.whatwg.org/multipage/#concept-dnd-ro>
+ #[allow(dead_code)] // TODO this used by ClipboardEvent.
+ ReadOnly,
+ /// <https://html.spec.whatwg.org/multipage/#concept-dnd-p>
+ Protected,
+}
+
+#[allow(dead_code)] // TODO some fields are used by DragEvent.
+pub struct DragDataStore {
+ /// <https://html.spec.whatwg.org/multipage/#drag-data-store-item-list>
+ item_list: Vec<Kind>,
+ /// <https://html.spec.whatwg.org/multipage/#drag-data-store-default-feedback>
+ default_feedback: Option<String>,
+ bitmap: Option<Bitmap>,
+ mode: Mode,
+ /// <https://html.spec.whatwg.org/multipage/#drag-data-store-allowed-effects-state>
+ allowed_effects_state: String,
+}
+
+impl DragDataStore {
+ /// <https://html.spec.whatwg.org/multipage/#create-a-drag-data-store>
+ // We don't really need it since it's only instantiated by DataTransfer.
+ #[allow(clippy::new_without_default)]
+ pub fn new() -> DragDataStore {
+ DragDataStore {
+ item_list: Vec::new(),
+ default_feedback: None,
+ bitmap: None,
+ mode: Mode::Protected,
+ allowed_effects_state: String::from("uninitialized"),
+ }
+ }
+
+ /// Get the drag data store mode
+ pub fn mode(&self) -> Mode {
+ self.mode
+ }
+
+ /// Set the drag data store mode
+ pub fn set_mode(&mut self, mode: Mode) {
+ self.mode = mode;
+ }
+
+ pub fn set_bitmap(&mut self, image: Option<Arc<Image>>, x: i32, y: i32) {
+ self.bitmap = Some(Bitmap { image, x, y });
+ }
+
+ /// <https://html.spec.whatwg.org/multipage/#concept-datatransfer-types>
+ pub fn types(&self) -> Vec<DOMString> {
+ let mut types = Vec::new();
+
+ let has_files = self.item_list.iter().fold(false, |has_files, item| {
+ // Step 2.1 For each item in the item list whose kind is text,
+ // add an entry to L consisting of the item's type string.
+ match item {
+ Kind::Text(string) => types.push(string.type_.clone()),
+ Kind::File(_) => return true,
+ }
+
+ has_files
+ });
+
+ // Step 2.2 If there are any items in the item list whose kind is File,
+ // add an entry to L consisting of the string "Files".
+ if has_files {
+ types.push(DOMString::from("Files"));
+ }
+ types
+ }
+
+ pub fn find_matching_text(&self, type_: &str) -> Option<DOMString> {
+ self.item_list
+ .iter()
+ .find(|item| item.text_type_matches(type_))
+ .and_then(|item| item.as_string())
+ }
+
+ pub fn add(&mut self, kind: Kind) -> Fallible<()> {
+ if let Kind::Text(ref string) = kind {
+ // Step 2.1 If there is already an item in the item list whose kind is text
+ // and whose type string is equal to the method's second argument, throw "NotSupportedError".
+ if self
+ .item_list
+ .iter()
+ .any(|item| item.text_type_matches(&string.type_))
+ {
+ return Err(Error::NotSupported);
+ }
+ }
+
+ // Step 2.2
+ self.item_list.push(kind);
+
+ Ok(())
+ }
+
+ pub fn set_data(&mut self, format: DOMString, data: DOMString) {
+ // Step 3-4
+ let type_ = normalize_mime(format);
+
+ // Step 5 Remove the item in the drag data store item list whose kind is text
+ // and whose type string is equal to format, if there is one.
+ self.item_list
+ .retain(|item| !item.text_type_matches(&type_));
+
+ // Step 6 Add an item whose kind is text, whose type is format, and whose data is the method's second argument.
+ self.item_list.push(Kind::Text(PlainString { data, type_ }));
+ }
+
+ pub fn clear_data(&mut self, format: Option<DOMString>) -> bool {
+ let mut was_modified = false;
+
+ if let Some(format) = format {
+ // Step 4-5
+ let type_ = normalize_mime(format);
+
+ // Step 6 Remove the item in the item list whose kind is text and whose type is format.
+ self.item_list.retain(|item| {
+ let matches = item.text_type_matches(&type_);
+
+ if matches {
+ was_modified = true;
+ }
+ !matches
+ });
+ } else {
+ // Step 3 Remove each item in the item list whose kind is text.
+ self.item_list.retain(|item| {
+ let matches = item.is_file();
+
+ if !matches {
+ was_modified = true;
+ }
+ matches
+ });
+ }
+
+ was_modified
+ }
+
+ pub fn files(&self, global: &GlobalScope, file_list: &mut Vec<DomRoot<File>>) {
+ // Step 3 If the data store is in the protected mode return the empty list.
+ if self.mode == Mode::Protected {
+ return;
+ }
+
+ // Step 4 For each item in the drag data store item list whose kind is File, add the item's data to the list L.
+ self.item_list
+ .iter()
+ .filter_map(|item| item.as_file(global))
+ .for_each(|file| file_list.push(file));
+ }
+
+ pub fn list_len(&self) -> usize {
+ self.item_list.len()
+ }
+
+ pub fn get_item(&self, index: usize) -> Option<Kind> {
+ self.item_list.get(index).cloned()
+ }
+
+ pub fn remove(&mut self, index: usize) {
+ self.item_list.remove(index);
+ }
+
+ pub fn clear_list(&mut self) {
+ self.item_list.clear();
+ }
+}
+
+fn normalize_mime(mut format: DOMString) -> DOMString {
+ // Convert format to ASCII lowercase.
+ format.make_ascii_lowercase();
+
+ match format.as_ref() {
+ // If format equals "text", change it to "text/plain".
+ "text" => DOMString::from("text/plain"),
+ // If format equals "url", change it to "text/uri-list".
+ "url" => DOMString::from("text/uri-list"),
+ s => DOMString::from(s),
+ }
+}