diff --git a/components/script/dom/datatransfer.rs b/components/script/dom/datatransfer.rs new file mode 100644 index 00000000000..f28d538ebd1 --- /dev/null +++ b/components/script/dom/datatransfer.rs @@ -0,0 +1,252 @@ +/* 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::cell::RefCell; +use std::rc::Rc; + +use dom_struct::dom_struct; +use js::rust::{HandleObject, MutableHandleValue}; + +use crate::dom::bindings::cell::DomRefCell; +use crate::dom::bindings::codegen::Bindings::DataTransferBinding::DataTransferMethods; +use crate::dom::bindings::inheritance::Castable; +use crate::dom::bindings::reflector::{reflect_dom_object_with_proto, DomObject, Reflector}; +use crate::dom::bindings::root::{Dom, DomRoot}; +use crate::dom::bindings::str::DOMString; +use crate::dom::datatransferitemlist::DataTransferItemList; +use crate::dom::element::Element; +use crate::dom::filelist::FileList; +use crate::dom::htmlimageelement::HTMLImageElement; +use crate::dom::window::Window; +use crate::drag_data_store::{DragDataStore, Mode}; +use crate::script_runtime::{CanGc, JSContext}; + +const VALID_DROP_EFFECTS: [&str; 4] = ["none", "copy", "link", "move"]; +const VALID_EFFECTS_ALLOWED: [&str; 9] = [ + "none", + "copy", + "copyLink", + "copyMove", + "link", + "linkMove", + "move", + "all", + "uninitialized", +]; + +#[dom_struct] +pub struct DataTransfer { + reflector_: Reflector, + drop_effect: DomRefCell, + effect_allowed: DomRefCell, + items: Dom, + #[ignore_malloc_size_of = "Rc"] + #[no_trace] + data_store: Rc>>, +} + +impl DataTransfer { + fn new_inherited( + data_store: Rc>>, + item_list: &DataTransferItemList, + ) -> DataTransfer { + DataTransfer { + reflector_: Reflector::new(), + drop_effect: DomRefCell::new(DOMString::from("none")), + effect_allowed: DomRefCell::new(DOMString::from("none")), + items: Dom::from_ref(item_list), + data_store, + } + } + + pub fn new_with_proto( + window: &Window, + proto: Option, + can_gc: CanGc, + ) -> DomRoot { + let mut drag_data_store = DragDataStore::new(); + drag_data_store.set_mode(Mode::ReadWrite); + + let data_store = Rc::new(RefCell::new(Some(drag_data_store))); + let item_list = DataTransferItemList::new(window, Rc::clone(&data_store)); + + reflect_dom_object_with_proto( + Box::new(DataTransfer::new_inherited(data_store, &item_list)), + window, + proto, + can_gc, + ) + } +} + +impl DataTransferMethods for DataTransfer { + /// + fn Constructor( + window: &Window, + proto: Option, + can_gc: CanGc, + ) -> DomRoot { + DataTransfer::new_with_proto(window, proto, can_gc) + } + + /// + fn DropEffect(&self) -> DOMString { + self.drop_effect.borrow().clone() + } + + /// + fn SetDropEffect(&self, value: DOMString) { + if VALID_DROP_EFFECTS.contains(&value.as_ref()) { + *self.drop_effect.borrow_mut() = value; + } + } + + /// + fn EffectAllowed(&self) -> DOMString { + self.effect_allowed.borrow().clone() + } + + /// + fn SetEffectAllowed(&self, value: DOMString) { + if self + .data_store + .borrow() + .as_ref() + .is_some_and(|data_store| data_store.mode() == Mode::ReadWrite) && + VALID_EFFECTS_ALLOWED.contains(&value.as_ref()) + { + *self.drop_effect.borrow_mut() = value; + } + } + + /// + fn Items(&self) -> DomRoot { + DomRoot::from_ref(&self.items) + } + + /// + fn SetDragImage(&self, image: &Element, x: i32, y: i32) { + // Step 1 If the DataTransfer is no longer associated with a data store, return. + let mut option = self.data_store.borrow_mut(); + let data_store = match option.as_mut() { + Some(value) => value, + None => return, + }; + + // Step 2 If the data store's mode is not the read/write mode, return. + if data_store.mode() != Mode::ReadWrite { + return; + } + + // Step 3 + if let Some(image) = image.downcast::() { + data_store.set_bitmap(image.image_data(), x, y); + } + } + + /// + fn Types(&self, cx: JSContext, retval: MutableHandleValue) { + self.items.frozen_types(cx, retval); + } + + /// + fn GetData(&self, mut format: DOMString) -> DOMString { + // Step 1 If the DataTransfer object is not associated with a drag data store, then return the empty string. + let option = self.data_store.borrow(); + let data_store = match option.as_ref() { + Some(value) => value, + None => return DOMString::new(), + }; + + // Step 2 If the drag data store's mode is the protected mode, then return the empty string. + if data_store.mode() == Mode::Protected { + return DOMString::new(); + } + + // Step 3 Let format be the first argument, converted to ASCII lowercase. + format.make_ascii_lowercase(); + // Step 4 Let convert-to-URL be false. + let mut convert_to_url = false; + + let type_ = match format.as_ref() { + // Step 5 If format equals "text", change it to "text/plain". + "text" => DOMString::from("text/plain"), + // Step 6 If format equals "url", change it to "text/uri-list" and set convert-to-URL to true. + "url" => { + convert_to_url = true; + DOMString::from("text/uri-list") + }, + _ => format, + }; + + let data = data_store.find_matching_text(&type_); + + // Step 8 + if let Some(result) = data { + // Step 9 If convert-to-URL is true, then parse result as appropriate for text/uri-list data, + // and then set result to the first URL from the list, if any, or the empty string otherwise. + if convert_to_url { + //TODO parse uri-list as [RFC2483] + } + + // Step 10 Return result. + result + } else { + // Step 7 If there is no item in the drag data store item list + // whose kind is text and whose type string is equal to format, return the empty string. + DOMString::new() + } + } + + /// + fn SetData(&self, format: DOMString, data: DOMString) { + // Step 1 If the DataTransfer object is no longer associated with a drag data store, return. Nothing happens. + let mut option = self.data_store.borrow_mut(); + let data_store = match option.as_mut() { + Some(value) => value, + None => return, + }; + + // Step 2 If the drag data store's mode is not the read/write mode, return. Nothing happens. + if data_store.mode() != Mode::ReadWrite { + return; + } + + data_store.set_data(format, data); + self.items.invalidate_frozen_types(); + } + + /// + fn ClearData(&self, format: Option) { + // Step 1 If the DataTransfer is not associated with a data store, return. + let mut option = self.data_store.borrow_mut(); + let data_store = match option.as_mut() { + Some(value) => value, + None => return, + }; + + // Step 2 If the data store is not in the read/write mode, return. + if data_store.mode() != Mode::ReadWrite { + return; + } + + if data_store.clear_data(format) { + self.items.invalidate_frozen_types(); + } + } + + /// + fn Files(&self) -> DomRoot { + // Step 1 Start with an empty list. + let mut files = Vec::new(); + + // Step 2 If the DataTransfer is not associated with a data store return the empty list. + if let Some(data_store) = self.data_store.borrow().as_ref() { + data_store.files(&self.global(), &mut files); + } + + // Step 5 + FileList::new(self.global().as_window(), files) + } +} diff --git a/components/script/dom/datatransferitem.rs b/components/script/dom/datatransferitem.rs new file mode 100644 index 00000000000..2144666421d --- /dev/null +++ b/components/script/dom/datatransferitem.rs @@ -0,0 +1,66 @@ +/* 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::rc::Rc; + +use dom_struct::dom_struct; + +use crate::dom::bindings::callback::ExceptionHandling; +use crate::dom::bindings::codegen::Bindings::DataTransferItemBinding::{ + DataTransferItemMethods, FunctionStringCallback, +}; +use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector}; +use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::str::DOMString; +use crate::dom::file::File; +use crate::dom::globalscope::GlobalScope; +use crate::drag_data_store::Kind; + +#[dom_struct] +pub struct DataTransferItem { + reflector_: Reflector, + #[ignore_malloc_size_of = "TODO"] + #[no_trace] + item: Kind, +} + +impl DataTransferItem { + fn new_inherited(item: Kind) -> DataTransferItem { + DataTransferItem { + reflector_: Reflector::new(), + item, + } + } + + pub fn new(global: &GlobalScope, item: Kind) -> DomRoot { + reflect_dom_object(Box::new(DataTransferItem::new_inherited(item)), global) + } +} + +impl DataTransferItemMethods for DataTransferItem { + /// + fn Kind(&self) -> DOMString { + match self.item { + Kind::Text(_) => DOMString::from("string"), + Kind::File(_) => DOMString::from("file"), + } + } + + /// + fn Type(&self) -> DOMString { + self.item.type_() + } + + /// + fn GetAsString(&self, callback: Option>) { + if let (Some(callback), Some(data)) = (callback, self.item.as_string()) { + let _ = callback.Call__(data, ExceptionHandling::Report); + } + } + + /// + fn GetAsFile(&self) -> Option> { + self.item.as_file(&self.global()) + } +} diff --git a/components/script/dom/datatransferitemlist.rs b/components/script/dom/datatransferitemlist.rs new file mode 100644 index 00000000000..9e2192e278b --- /dev/null +++ b/components/script/dom/datatransferitemlist.rs @@ -0,0 +1,199 @@ +/* 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::cell::RefCell; +use std::rc::Rc; + +use dom_struct::dom_struct; +use js::rust::MutableHandleValue; + +use crate::dom::bindings::codegen::Bindings::DataTransferItemListBinding::DataTransferItemListMethods; +use crate::dom::bindings::error::{Error, Fallible}; +use crate::dom::bindings::frozenarray::CachedFrozenArray; +use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector}; +use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::str::DOMString; +use crate::dom::datatransferitem::DataTransferItem; +use crate::dom::file::File; +use crate::dom::window::Window; +use crate::drag_data_store::{Binary, DragDataStore, Kind, Mode, PlainString}; +use crate::script_runtime::JSContext; + +#[dom_struct] +pub struct DataTransferItemList { + reflector_: Reflector, + #[ignore_malloc_size_of = "Rc"] + #[no_trace] + data_store: Rc>>, + #[ignore_malloc_size_of = "mozjs"] + frozen_types: CachedFrozenArray, +} + +impl DataTransferItemList { + fn new_inherited(data_store: Rc>>) -> DataTransferItemList { + DataTransferItemList { + reflector_: Reflector::new(), + frozen_types: CachedFrozenArray::new(), + data_store, + } + } + + pub fn new( + window: &Window, + data_store: Rc>>, + ) -> DomRoot { + reflect_dom_object( + Box::new(DataTransferItemList::new_inherited(data_store)), + window, + ) + } + + pub fn frozen_types(&self, cx: JSContext, retval: MutableHandleValue) { + self.frozen_types.get_or_init( + || { + self.data_store + .borrow() + .as_ref() + .map_or(Vec::new(), |data_store| data_store.types()) + }, + cx, + retval, + ); + } + + pub fn invalidate_frozen_types(&self) { + self.frozen_types.clear(); + } +} + +impl DataTransferItemListMethods for DataTransferItemList { + /// + fn Length(&self) -> u32 { + // Return zero if the object is in the disabled mode; + // otherwise it must return the number of items in the drag data store item list. + self.data_store + .borrow() + .as_ref() + .map_or(0, |data_store| data_store.list_len() as u32) + } + + /// + fn IndexedGetter(&self, index: u32) -> Option> { + // Step 1 Return null if it isn't associated with a data store + let option = self.data_store.borrow(); + let data_store = match option.as_ref() { + Some(value) => value, + _ => return None, + }; + + // Step 2 + data_store + .get_item(index as usize) + .map(|item| DataTransferItem::new(&self.global(), item)) + } + + /// + fn Add( + &self, + data: DOMString, + mut type_: DOMString, + ) -> Fallible>> { + // Step 1 If the DataTransferItemList object is not in the read/write mode, return null. + let mut option = self.data_store.borrow_mut(); + let data_store = match option.as_mut() { + Some(value) if value.mode() == Mode::ReadWrite => value, + _ => return Ok(None), + }; + + // Add an item to the drag data store item list whose kind is text, + // whose type string is equal to the value of the method's second argument, converted to ASCII lowercase, + // and whose data is the string given by the method's first argument. + type_.make_ascii_lowercase(); + data_store.add(Kind::Text(PlainString::new(data, type_)))?; + + self.frozen_types.clear(); + + // Step 3 Determine the value of the indexed property corresponding to the newly added item, + // and return that value (a newly created DataTransferItem object). + let index = data_store.list_len() - 1; + let item = data_store + .get_item(index) + .map(|item| DataTransferItem::new(&self.global(), item)); + + Ok(item) + } + + /// + fn Add_(&self, data: &File) -> Fallible>> { + // Step 1 If the DataTransferItemList object is not in the read/write mode, return null. + let mut option = self.data_store.borrow_mut(); + let data_store = match option.as_mut() { + Some(value) if value.mode() == Mode::ReadWrite => value, + _ => return Ok(None), + }; + + // Add an item to the drag data store item list whose kind is File, + // whose type string is the type of the File, converted to ASCII lowercase, + // and whose data is the same as the File's data. + let mut type_ = data.file_type(); + type_.make_ascii_lowercase(); + let binary = Binary::new( + data.file_bytes().unwrap_or_default(), + data.name().clone(), + type_, + ); + + data_store.add(Kind::File(binary))?; + + self.frozen_types.clear(); + + // Step 3 Determine the value of the indexed property corresponding to the newly added item, + // and return that value (a newly created DataTransferItem object). + let index = data_store.list_len() - 1; + let item = data_store + .get_item(index) + .map(|item| DataTransferItem::new(&self.global(), item)); + + Ok(item) + } + + /// + fn Remove(&self, index: u32) -> Fallible<()> { + // Step 1 If the DataTransferItemList object is not in the read/write mode, + // throw an "InvalidStateError" DOMException. + let mut option = self.data_store.borrow_mut(); + let data_store = match option.as_mut() { + Some(value) if value.mode() == Mode::ReadWrite => value, + _ => return Err(Error::InvalidState), + }; + + let index = index as usize; + + // Step 2 If the drag data store does not contain an indexth item, then return. + if index < data_store.list_len() { + // Step 3 Remove the indexth item from the drag data store. + data_store.remove(index); + self.frozen_types.clear(); + } + + Ok(()) + } + + /// + fn Clear(&self) { + // If the DataTransferItemList object is in the read/write mode, remove all the items from the drag data store. + // Otherwise, it must do nothing. + let mut option = self.data_store.borrow_mut(); + let data_store = match option.as_mut() { + Some(value) if value.mode() == Mode::ReadWrite => value, + _ => return, + }; + + // If the item list is empty we don't clear it. + if data_store.list_len() > 0 { + data_store.clear_list(); + self.frozen_types.clear(); + } + } +} diff --git a/components/script/dom/file.rs b/components/script/dom/file.rs index 5fa55ddf4f5..7863ea5bf59 100644 --- a/components/script/dom/file.rs +++ b/components/script/dom/file.rs @@ -97,9 +97,17 @@ impl File { ) } + pub fn file_bytes(&self) -> Result, ()> { + self.blob.get_bytes() + } + pub fn name(&self) -> &DOMString { &self.name } + + pub fn file_type(&self) -> String { + self.blob.type_string() + } } impl FileMethods for File { diff --git a/components/script/dom/htmlimageelement.rs b/components/script/dom/htmlimageelement.rs index 2bb1277c987..112115e0442 100644 --- a/components/script/dom/htmlimageelement.rs +++ b/components/script/dom/htmlimageelement.rs @@ -200,6 +200,10 @@ impl HTMLImageElement { State::PartiallyAvailable | State::Unavailable => Ok(false), } } + + pub fn image_data(&self) -> Option> { + self.current_request.borrow().image.clone() + } } /// The context required for asynchronously loading an external image. diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index 3deecb1cb0d..3631b553f2d 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -278,6 +278,9 @@ pub mod cssstylevalue; pub mod csssupportsrule; pub mod customelementregistry; pub mod customevent; +pub mod datatransfer; +pub mod datatransferitem; +pub mod datatransferitemlist; pub mod dedicatedworkerglobalscope; pub mod dissimilaroriginlocation; pub mod dissimilaroriginwindow; diff --git a/components/script/dom/webidls/DataTransfer.webidl b/components/script/dom/webidls/DataTransfer.webidl new file mode 100644 index 00000000000..2e546f0c579 --- /dev/null +++ b/components/script/dom/webidls/DataTransfer.webidl @@ -0,0 +1,24 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ + +// https://html.spec.whatwg.org/multipage/#datatransfer + +[Exposed=Window] +interface DataTransfer { + constructor(); + + attribute DOMString dropEffect; + attribute DOMString effectAllowed; + + [SameObject] readonly attribute DataTransferItemList items; + + undefined setDragImage(Element image, long x, long y); + + /* old interface */ + readonly attribute /* FrozenArray */ any types; + DOMString getData(DOMString format); + undefined setData(DOMString format, DOMString data); + undefined clearData(optional DOMString format); + [SameObject] readonly attribute FileList files; +}; diff --git a/components/script/dom/webidls/DataTransferItem.webidl b/components/script/dom/webidls/DataTransferItem.webidl new file mode 100644 index 00000000000..56008f13715 --- /dev/null +++ b/components/script/dom/webidls/DataTransferItem.webidl @@ -0,0 +1,15 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ + +// https://html.spec.whatwg.org/multipage/#datatransferitem + +[Exposed=Window] +interface DataTransferItem { + readonly attribute DOMString kind; + readonly attribute DOMString type; + undefined getAsString(FunctionStringCallback? _callback); + File? getAsFile(); +}; + +callback FunctionStringCallback = undefined (DOMString data); diff --git a/components/script/dom/webidls/DataTransferItemList.webidl b/components/script/dom/webidls/DataTransferItemList.webidl new file mode 100644 index 00000000000..efdcb72806e --- /dev/null +++ b/components/script/dom/webidls/DataTransferItemList.webidl @@ -0,0 +1,15 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ + +// https://html.spec.whatwg.org/multipage/#datatransferitemlist + +[Exposed=Window] +interface DataTransferItemList { + readonly attribute unsigned long length; + getter DataTransferItem (unsigned long index); + [Throws] DataTransferItem? add(DOMString data, DOMString type); + [Throws] DataTransferItem? add(File data); + [Throws] undefined remove(unsigned long index); + undefined clear(); +}; 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; + +/// +#[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, + name: DOMString, + type_: String, +} + +impl Binary { + pub fn new(bytes: Vec, 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 { + 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> { + 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(_)) + } +} + +/// +#[allow(dead_code)] // TODO this used by DragEvent. +struct Bitmap { + image: Option>, + x: i32, + y: i32, +} + +/// Control the behaviour of the drag data store +#[derive(Clone, Copy, Eq, PartialEq)] +pub enum Mode { + /// + ReadWrite, + /// + #[allow(dead_code)] // TODO this used by ClipboardEvent. + ReadOnly, + /// + Protected, +} + +#[allow(dead_code)] // TODO some fields are used by DragEvent. +pub struct DragDataStore { + /// + item_list: Vec, + /// + default_feedback: Option, + bitmap: Option, + mode: Mode, + /// + allowed_effects_state: String, +} + +impl DragDataStore { + /// + // 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>, x: i32, y: i32) { + self.bitmap = Some(Bitmap { image, x, y }); + } + + /// + pub fn types(&self) -> Vec { + 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 { + 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) -> 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>) { + // 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 { + 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), + } +} diff --git a/components/script/lib.rs b/components/script/lib.rs index 42f3d2c0c4c..63f0147d421 100644 --- a/components/script/lib.rs +++ b/components/script/lib.rs @@ -96,6 +96,7 @@ mod window_named_properties; mod unminify; +mod drag_data_store; mod links; pub use init::init; diff --git a/tests/wpt/meta/html/dom/idlharness.https.html.ini b/tests/wpt/meta/html/dom/idlharness.https.html.ini index 82cc0be8c16..eedf2e1f139 100644 --- a/tests/wpt/meta/html/dom/idlharness.https.html.ini +++ b/tests/wpt/meta/html/dom/idlharness.https.html.ini @@ -5042,114 +5042,6 @@ [CloseWatcher interface: attribute onclose] expected: FAIL - [DataTransfer interface: existence and properties of interface object] - expected: FAIL - - [DataTransfer interface object length] - expected: FAIL - - [DataTransfer interface object name] - expected: FAIL - - [DataTransfer interface: existence and properties of interface prototype object] - expected: FAIL - - [DataTransfer interface: existence and properties of interface prototype object's "constructor" property] - expected: FAIL - - [DataTransfer interface: existence and properties of interface prototype object's @@unscopables property] - expected: FAIL - - [DataTransfer interface: attribute dropEffect] - expected: FAIL - - [DataTransfer interface: attribute effectAllowed] - expected: FAIL - - [DataTransfer interface: attribute items] - expected: FAIL - - [DataTransfer interface: operation setDragImage(Element, long, long)] - expected: FAIL - - [DataTransfer interface: attribute types] - expected: FAIL - - [DataTransfer interface: operation getData(DOMString)] - expected: FAIL - - [DataTransfer interface: operation setData(DOMString, DOMString)] - expected: FAIL - - [DataTransfer interface: operation clearData(optional DOMString)] - expected: FAIL - - [DataTransfer interface: attribute files] - expected: FAIL - - [DataTransferItemList interface: existence and properties of interface object] - expected: FAIL - - [DataTransferItemList interface object length] - expected: FAIL - - [DataTransferItemList interface object name] - expected: FAIL - - [DataTransferItemList interface: existence and properties of interface prototype object] - expected: FAIL - - [DataTransferItemList interface: existence and properties of interface prototype object's "constructor" property] - expected: FAIL - - [DataTransferItemList interface: existence and properties of interface prototype object's @@unscopables property] - expected: FAIL - - [DataTransferItemList interface: attribute length] - expected: FAIL - - [DataTransferItemList interface: operation add(DOMString, DOMString)] - expected: FAIL - - [DataTransferItemList interface: operation add(File)] - expected: FAIL - - [DataTransferItemList interface: operation remove(unsigned long)] - expected: FAIL - - [DataTransferItemList interface: operation clear()] - expected: FAIL - - [DataTransferItem interface: existence and properties of interface object] - expected: FAIL - - [DataTransferItem interface object length] - expected: FAIL - - [DataTransferItem interface object name] - expected: FAIL - - [DataTransferItem interface: existence and properties of interface prototype object] - expected: FAIL - - [DataTransferItem interface: existence and properties of interface prototype object's "constructor" property] - expected: FAIL - - [DataTransferItem interface: existence and properties of interface prototype object's @@unscopables property] - expected: FAIL - - [DataTransferItem interface: attribute kind] - expected: FAIL - - [DataTransferItem interface: attribute type] - expected: FAIL - - [DataTransferItem interface: operation getAsString(FunctionStringCallback?)] - expected: FAIL - - [DataTransferItem interface: operation getAsFile()] - expected: FAIL - [DragEvent interface: existence and properties of interface object] expected: FAIL diff --git a/tests/wpt/meta/html/editing/dnd/datastore/datatransfer-constructor-001.html.ini b/tests/wpt/meta/html/editing/dnd/datastore/datatransfer-constructor-001.html.ini deleted file mode 100644 index 750d47ebed2..00000000000 --- a/tests/wpt/meta/html/editing/dnd/datastore/datatransfer-constructor-001.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[datatransfer-constructor-001.html] - [Verify DataTransfer constructor] - expected: FAIL diff --git a/tests/wpt/meta/html/editing/dnd/datastore/datatransfer-types.html.ini b/tests/wpt/meta/html/editing/dnd/datastore/datatransfer-types.html.ini deleted file mode 100644 index 0b373dd8da2..00000000000 --- a/tests/wpt/meta/html/editing/dnd/datastore/datatransfer-types.html.ini +++ /dev/null @@ -1,15 +0,0 @@ -[datatransfer-types.html] - [type's state on DataTransfer creation] - expected: FAIL - - [Relationship between types and items] - expected: FAIL - - [type's identity] - expected: FAIL - - [Verify type is a read-only attribute] - expected: FAIL - - [DataTransfer containing files] - expected: FAIL diff --git a/tests/wpt/meta/html/editing/dnd/datastore/datatransferitemlist-remove.html.ini b/tests/wpt/meta/html/editing/dnd/datastore/datatransferitemlist-remove.html.ini deleted file mode 100644 index e4a19e7595e..00000000000 --- a/tests/wpt/meta/html/editing/dnd/datastore/datatransferitemlist-remove.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[datatransferitemlist-remove.html] - [remove()ing an out-of-bounds index does nothing] - expected: FAIL diff --git a/tests/wpt/meta/html/semantics/forms/form-submission-0/newline-normalization.html.ini b/tests/wpt/meta/html/semantics/forms/form-submission-0/newline-normalization.html.ini index 1cc138175d3..8144a10f25d 100644 --- a/tests/wpt/meta/html/semantics/forms/form-submission-0/newline-normalization.html.ini +++ b/tests/wpt/meta/html/semantics/forms/form-submission-0/newline-normalization.html.ini @@ -22,15 +22,3 @@ [Form newline normalization: \\n\\r in the filename stays unchanged] expected: FAIL - - [Constructing the entry list shouldn't perform newline normalization: \\n in the filename] - expected: FAIL - - [Constructing the entry list shouldn't perform newline normalization: \\r in the filename] - expected: FAIL - - [Constructing the entry list shouldn't perform newline normalization: \\r\\n in the filename] - expected: FAIL - - [Constructing the entry list shouldn't perform newline normalization: \\n\\r in the filename] - expected: FAIL diff --git a/tests/wpt/meta/html/semantics/forms/form-submission-0/submit-file.sub.html.ini b/tests/wpt/meta/html/semantics/forms/form-submission-0/submit-file.sub.html.ini deleted file mode 100644 index a2d487c0e97..00000000000 --- a/tests/wpt/meta/html/semantics/forms/form-submission-0/submit-file.sub.html.ini +++ /dev/null @@ -1,4 +0,0 @@ -[submit-file.sub.html] - [Posting a File] - expected: FAIL - diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index c1c95016bcb..094659d27ca 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -13495,7 +13495,7 @@ ] ], "interfaces.https.html": [ - "5a39868cd2090b492e2192447bf353527649bbfb", + "dc853b1a823990fa2d1478533dc5bd17fb576a13", [ null, {} diff --git a/tests/wpt/mozilla/tests/mozilla/interfaces.https.html b/tests/wpt/mozilla/tests/mozilla/interfaces.https.html index 5a39868cd20..dc853b1a823 100644 --- a/tests/wpt/mozilla/tests/mozilla/interfaces.https.html +++ b/tests/wpt/mozilla/tests/mozilla/interfaces.https.html @@ -58,6 +58,9 @@ test_interfaces([ "CSSStyleRule", "CSSStyleSheet", "CSSSupportsRule", + "DataTransfer", + "DataTransferItem", + "DataTransferItemList", "DOMMatrix", "DOMMatrixReadOnly", "DOMPoint",