View Javadoc
1   /*
2    * Copyright (c) 2002-2026 Gargoyle Software Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * https://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  package org.htmlunit.javascript.host.draganddrop;
16  
17  import java.util.ArrayList;
18  
19  import org.htmlunit.corejs.javascript.Context;
20  import org.htmlunit.corejs.javascript.Function;
21  import org.htmlunit.corejs.javascript.Scriptable;
22  import org.htmlunit.corejs.javascript.VarScope;
23  import org.htmlunit.javascript.HtmlUnitScriptable;
24  import org.htmlunit.javascript.JavaScriptEngine;
25  import org.htmlunit.javascript.configuration.JsxClass;
26  import org.htmlunit.javascript.configuration.JsxConstructor;
27  import org.htmlunit.javascript.configuration.JsxFunction;
28  import org.htmlunit.javascript.configuration.JsxGetter;
29  import org.htmlunit.javascript.configuration.JsxSymbol;
30  import org.htmlunit.javascript.host.file.File;
31  import org.htmlunit.javascript.host.file.FileList;
32  
33  /**
34   * A JavaScript object for {@code DataTransferItemList}.
35   *
36   * @author Ahmed Ashour
37   * @author Ronald Brill
38   */
39  @JsxClass
40  public class DataTransferItemList extends HtmlUnitScriptable {
41  
42      private ArrayList<DataTransferItem> items_;
43      private FileList fileList_;
44  
45      /**
46       * JavaScript constructor.
47       */
48      @JsxConstructor
49      public void jsConstructor() {
50          // nothing to do
51      }
52  
53      /**
54       * Returns the {@code length} property.
55       *
56       * @return the {@code length} property
57       */
58      @JsxGetter
59      public int getLength() {
60          if (items_ == null) {
61              return 0;
62          }
63          return items_.size();
64      }
65  
66      /**
67       * Creates a new {@link DataTransferItem} using the specified data and adds it to the drag data list.
68       * The item may be a {@link File} or a string of a given type. If the item is successfully added to the list,
69       * the newly-created {@link DataTransferItem} object is returned.
70       *
71       * @param context the JavaScript context
72       * @param scope the scope
73       * @param thisObj the scriptable
74       * @param args the arguments passed into the method
75       * @param function the function
76       * @return the newly-created {@link DataTransferItem} object
77       * @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/DataTransferItemList/add">MDN documentation</a>
78       */
79      @JsxFunction
80      public static DataTransferItem add(final Context context, final VarScope scope,
81              final Scriptable thisObj, final Object[] args, final Function function) {
82          final DataTransferItemList itemList = (DataTransferItemList) thisObj;
83          if (args.length == 1) {
84              if (args[0] instanceof File file) {
85                  final DataTransferItem item = DataTransferItem.buildFileItem(file);
86                  item.setParentScope(scope);
87                  item.setPrototype(itemList.getPrototype(item.getClass()));
88  
89                  if (itemList.items_ == null) {
90                      itemList.items_ = new ArrayList<>();
91                  }
92                  itemList.items_.add(item);
93                  itemList.updateFileList();
94  
95                  return item;
96              }
97              throw JavaScriptEngine.typeError(
98                      "Failed to execute 'add' on 'DataTransferItemList': parameter 1 is not of type 'File'.");
99          }
100 
101         if (args.length > 1) {
102             final String data = JavaScriptEngine.toString(args[0]);
103             final String type = JavaScriptEngine.toString(args[1]);
104             final DataTransferItem item = DataTransferItem.buildStringItem(data, type);
105             item.setParentScope(scope);
106             item.setPrototype(itemList.getPrototype(item.getClass()));
107 
108             if (itemList.items_ == null) {
109                 itemList.items_ = new ArrayList<>();
110             }
111             itemList.items_.add(item);
112 
113             return item;
114         }
115 
116         throw JavaScriptEngine.typeError(
117                 "Failed to execute 'add' on 'DataTransferItemList' - no args provided.");
118     }
119 
120     /**
121      * Removes all DataTransferItem objects from the drag data items list, leaving the list empty.
122      */
123     @JsxFunction
124     public void clear() {
125         if (items_ != null) {
126             items_.clear();
127             if (fileList_ != null) {
128                 fileList_.updateFiles(new ArrayList<>());
129             }
130         }
131     }
132 
133     /**
134      * Removes the DataTransferItem at the specified index from the list. If the index is less
135      * than zero or greater than one less than the length of the list, the list will not be changed.
136      *
137      * @param index the zero-based index number of the item in the drag data list to remove.
138      *        If the index doesn't correspond to an existing item in the list, the list is left unchanged.
139      */
140     @JsxFunction
141     public void remove(final int index) {
142         if (items_ != null) {
143             if (index >= 0 && index < items_.size()) {
144                 items_.remove(index);
145                 updateFileList();
146             }
147         }
148     }
149 
150     /**
151      * {@inheritDoc}
152      */
153     @Override
154     public Object get(final int index, final Scriptable start) {
155         if (this == start) {
156             if (index >= 0 && index < items_.size()) {
157                 return items_.get(index);
158             }
159         }
160         return super.get(index, start);
161     }
162 
163     /**
164      * Returns an iterator over the values in this object.
165      *
166      * @return a native array iterator
167      */
168     @JsxSymbol(symbolName = "iterator")
169     public Scriptable values() {
170         return JavaScriptEngine.newArrayIteratorTypeValues(getParentScope(), this);
171     }
172 
173     /**
174      * Returns the file list for the parent {@code DataTransfer} object.
175      * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span>
176      *
177      * @return the {@code files} property
178      */
179     public FileList getFiles() {
180         if (fileList_ == null) {
181             final FileList list = new FileList(new java.io.File[0]);
182             list.setParentScope(getParentScope());
183             list.setPrototype(getPrototype(list.getClass()));
184             fileList_ = list;
185 
186             if (items_ != null) {
187                 updateFileList();
188             }
189         }
190         return fileList_;
191     }
192 
193     private void updateFileList() {
194         if (fileList_ != null) {
195             final ArrayList<File> files = new ArrayList<>();
196             for (final DataTransferItem item : items_) {
197                 if (item.isFile()) {
198                     files.add(item.getAsFile());
199                 }
200             }
201             fileList_.updateFiles(files);
202         }
203     }
204 }