Explorar o código

LibWeb: Implement DataTransferItemList.prototype.add()

Timothy Flynn hai 10 meses
pai
achega
74d9cfbf2a

+ 1 - 0
Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp

@@ -53,6 +53,7 @@ static bool is_platform_object(Type const& type)
         "DynamicsCompressorNode"sv,
         "ElementInternals"sv,
         "EventTarget"sv,
+        "File"sv,
         "FileList"sv,
         "FontFace"sv,
         "FormData"sv,

+ 2 - 0
Tests/LibWeb/Text/expected/HTML/data-transfer.txt

@@ -1,2 +1,4 @@
 dropEffect: none
 effectAllowed: none
+stringItem: [object DataTransferItem], types=custom-type
+fileItem: [object DataTransferItem], types=custom-type,Files

+ 17 - 0
Tests/LibWeb/Text/input/HTML/data-transfer.html

@@ -4,5 +4,22 @@
         let dataTransfer = new DataTransfer();
         println(`dropEffect: ${dataTransfer.dropEffect}`);
         println(`effectAllowed: ${dataTransfer.effectAllowed}`);
+
+        let dataTransferItemList = dataTransfer.items;
+
+        let stringItem = dataTransferItemList.add("well hello friends", "custom-type");
+        println(`stringItem: ${stringItem}, types=${dataTransfer.types}`);
+
+        try {
+            dataTransferItemList.add("well hello friends", "custom-type");
+            println("FAILED");
+        } catch (e) {}
+
+        let file = new File(["well hello friends"], "file.txt", {
+            type: "text/plain",
+        });
+
+        let fileItem = dataTransferItemList.add(file);
+        println(`fileItem: ${fileItem}, types=${dataTransfer.types}`);
     });
 </script>

+ 34 - 1
Userland/Libraries/LibWeb/HTML/DataTransfer.cpp

@@ -220,12 +220,46 @@ JS::NonnullGCPtr<FileAPI::FileList> DataTransfer::files() const
     return files;
 }
 
+Optional<DragDataStore::Mode> DataTransfer::mode() const
+{
+    if (m_associated_drag_data_store)
+        return m_associated_drag_data_store->mode();
+    return {};
+}
+
 void DataTransfer::disassociate_with_drag_data_store()
 {
     m_associated_drag_data_store.clear();
     update_data_transfer_types_list();
 }
 
+JS::NonnullGCPtr<DataTransferItem> DataTransfer::add_item(DragDataStoreItem item)
+{
+    auto& realm = this->realm();
+
+    VERIFY(m_associated_drag_data_store);
+    m_associated_drag_data_store->add_item(move(item));
+
+    auto data_transfer_item = DataTransferItem::create(realm, *this, m_associated_drag_data_store->size() - 1);
+    m_item_list.append(data_transfer_item);
+
+    update_data_transfer_types_list();
+
+    return data_transfer_item;
+}
+
+bool DataTransfer::contains_item_with_type(DragDataStoreItem::Kind kind, String const& type) const
+{
+    VERIFY(m_associated_drag_data_store);
+
+    for (auto const& item : m_associated_drag_data_store->item_list()) {
+        if (item.kind == kind && item.type_string.equals_ignoring_ascii_case(type))
+            return true;
+    }
+
+    return false;
+}
+
 // https://html.spec.whatwg.org/multipage/dnd.html#concept-datatransfer-types
 void DataTransfer::update_data_transfer_types_list()
 {
@@ -259,5 +293,4 @@ void DataTransfer::update_data_transfer_types_list()
     // 3. Set the DataTransfer object's types array to the result of creating a frozen array from L.
     m_types = move(types);
 }
-
 }

+ 4 - 0
Userland/Libraries/LibWeb/HTML/DataTransfer.h

@@ -56,8 +56,12 @@ public:
     String get_data(String const& format) const;
     JS::NonnullGCPtr<FileAPI::FileList> files() const;
 
+    Optional<DragDataStore::Mode> mode() const;
     void disassociate_with_drag_data_store();
 
+    JS::NonnullGCPtr<DataTransferItem> add_item(DragDataStoreItem item);
+    bool contains_item_with_type(DragDataStoreItem::Kind, String const& type) const;
+
 private:
     DataTransfer(JS::Realm&, NonnullRefPtr<DragDataStore>);
 

+ 61 - 0
Userland/Libraries/LibWeb/HTML/DataTransferItemList.cpp

@@ -7,8 +7,10 @@
 #include <LibJS/Runtime/Realm.h>
 #include <LibWeb/Bindings/DataTransferItemListPrototype.h>
 #include <LibWeb/Bindings/Intrinsics.h>
+#include <LibWeb/FileAPI/File.h>
 #include <LibWeb/HTML/DataTransfer.h>
 #include <LibWeb/HTML/DataTransferItemList.h>
+#include <LibWeb/Infra/Strings.h>
 
 namespace Web::HTML {
 
@@ -39,4 +41,63 @@ void DataTransferItemList::visit_edges(JS::Cell::Visitor& visitor)
     visitor.visit(m_data_transfer);
 }
 
+// https://html.spec.whatwg.org/multipage/dnd.html#dom-datatransferitemlist-add
+WebIDL::ExceptionOr<JS::GCPtr<DataTransferItem>> DataTransferItemList::add(String const& data, String const& type)
+{
+    auto& realm = this->realm();
+
+    // 1. If the DataTransferItemList object is not in the read/write mode, return null.
+    if (m_data_transfer->mode() != DragDataStore::Mode::ReadWrite)
+        return nullptr;
+
+    // 2. Jump to the appropriate set of steps from the following list:
+    //    -> If the first argument to the method is a string
+
+    // If there is already an item in the drag data store item list whose kind is text and whose type string is equal
+    // to the value of the method's second argument, converted to ASCII lowercase, then throw a "NotSupportedError"
+    // DOMException.
+    if (m_data_transfer->contains_item_with_type(DragDataStoreItem::Kind::Text, type)) {
+        auto error = MUST(String::formatted("There is already a DataTransferItem with type {}", type));
+        return WebIDL::NotSupportedError::create(realm, error);
+    }
+
+    // Otherwise, 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.
+    auto item = m_data_transfer->add_item({
+        .kind = HTML::DragDataStoreItem::Kind::Text,
+        .type_string = MUST(Infra::to_ascii_lowercase(type)),
+        .data = MUST(ByteBuffer::copy(data.bytes())),
+        .file_name = {},
+    });
+
+    // 3. Determine the value of the indexed property corresponding to the newly added item, and return that value (a
+    //    newly created DataTransferItem object).
+    return item;
+}
+
+// https://html.spec.whatwg.org/multipage/dnd.html#dom-datatransferitemlist-add
+JS::GCPtr<DataTransferItem> DataTransferItemList::add(JS::NonnullGCPtr<FileAPI::File> file)
+{
+    // 1. If the DataTransferItemList object is not in the read/write mode, return null.
+    if (m_data_transfer->mode() != DragDataStore::Mode::ReadWrite)
+        return nullptr;
+
+    // 2. Jump to the appropriate set of steps from the following list:
+    //     -> If the first argument to the method is a File
+
+    // 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.
+    auto item = m_data_transfer->add_item({
+        .kind = HTML::DragDataStoreItem::Kind::File,
+        .type_string = MUST(Infra::to_ascii_lowercase(file->type())),
+        .data = MUST(ByteBuffer::copy(file->raw_bytes())),
+        .file_name = file->name().to_byte_string(),
+    });
+
+    // 3. Determine the value of the indexed property corresponding to the newly added item, and return that value (a
+    //    newly created DataTransferItem object).
+    return item;
+}
+
 }

+ 4 - 0
Userland/Libraries/LibWeb/HTML/DataTransferItemList.h

@@ -8,6 +8,7 @@
 
 #include <LibJS/Forward.h>
 #include <LibWeb/Bindings/PlatformObject.h>
+#include <LibWeb/WebIDL/ExceptionOr.h>
 
 namespace Web::HTML {
 
@@ -20,6 +21,9 @@ public:
     static JS::NonnullGCPtr<DataTransferItemList> create(JS::Realm&, JS::NonnullGCPtr<DataTransfer>);
     virtual ~DataTransferItemList() override;
 
+    WebIDL::ExceptionOr<JS::GCPtr<DataTransferItem>> add(String const& data, String const& type);
+    JS::GCPtr<DataTransferItem> add(JS::NonnullGCPtr<FileAPI::File>);
+
 private:
     DataTransferItemList(JS::Realm&, JS::NonnullGCPtr<DataTransfer>);
 

+ 3 - 2
Userland/Libraries/LibWeb/HTML/DataTransferItemList.idl

@@ -1,3 +1,4 @@
+#import <FileAPI/File.idl>
 #import <HTML/DataTransferItem.idl>
 
 // https://html.spec.whatwg.org/multipage/dnd.html#datatransferitemlist
@@ -5,8 +6,8 @@
 interface DataTransferItemList {
     [FIXME] readonly attribute unsigned long length;
     [FIXME] getter DataTransferItem (unsigned long index);
-    [FIXME] DataTransferItem? add(DOMString data, DOMString type);
-    [FIXME] DataTransferItem? add(File data);
+    DataTransferItem? add(DOMString data, DOMString type);
+    DataTransferItem? add(File data);
     [FIXME] undefined remove(unsigned long index);
     [FIXME] undefined clear();
 };