New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make FileList mutable. #3269
Comments
Since this primarily affects drag & drop and |
(I don't see any security implications by the way. You can already recreate most of the setup with |
For the list of Blob objects how could the filenames be specified? |
It seems that we do now have (1) a way to construct an empty function newFileList() {
const dt = new DataTransfer();
if (arguments[0] !== undefined) {
for (const file of arguments[0]) dt.items.add(file);
}
const fl = dt.files;
Object.defineProperty(fl, 'append', {value: file => { dt.items.add(file); }});
return fl;
} Note the Conceptually I'd be comfortable adding |
We could also add [LegacyArrayClass] and an indexed setter, to give it all the array methods. Then it would be push(), not append(). |
I was thinking if you add I thought we were trying to get rid of [LegacyArrayClass] still? Also, is this a list or a set? Can you get the same File object in there twice? I guess maybe with drag & drop now it has this API form we didn't fully consider when adding, but not with If we settle list/set I think the most conservative v0 would be solely a constructor and leave mutation for a later version. That's how we approached |
Oh, and let's copy @guest271314 since they apparently discovered this and are therefore probably interested. |
FWIW, there was a way to get File instances from the File constructor into FileList before the DataTransfer constructor (at least in Chrome), but it required a drag-and-drop gesture -- see https://crbug.com/367334 I am strongly in favor of adding a straightforward way to build a FileList out of a sequence of Files, or adding a way to set an I weakly prefer that FileList remains / becomes immutable, to avoid complexity in the conceptual model for |
@annevk Is the dispatching of Should directory uploads also be considered? That is to also have the ability to create a What would be the practical difference between |
@guest271314 the change event on HTMLInputElement is triggered by end-user's action. Just like if you change any value of any type of input, this event should not fire when the change in value has been triggered by script. As to your concern about Set vs List, even with the webkitDirectory attribute, you can not have twice the same File from usual input interaction. Your two copies example are still two differents File objects, at least with their own |
@Kaiido Note, |
@guest271314 and they don't when you clear this FileList through https://html.spec.whatwg.org/multipage/indices.html#event-change
You might want to open an issue to bugs.chromium and bugs.webkit since it seems this was introduced prior to Blink split. |
@guest271314 I meant to get back to you on that, sorry for not having done so yet, but it seems you resolved it yourself together with @Kaiido by filing those bugs. Hopefully they can be fixed but otherwise we'll need a new issue to sort that out. Thank you for taking care of that! As for directories, they're separate. We will need to integrate that document into the HTML Standard in due course. Any new features can be considered after that or potentially in parallel by filing issues against that document. As for set vs list. Consider |
@annevk The only security issue that could fathom is a malicious script which switched an uploaded file initiated by user action to attribute the file to a user where the remainder of the form was attributed to the user - based on the initial user action of uploading the file that the user selected from a file system. Though for practical purposes that would not necessarily matter because the As to whether or not |
A developer cannot observe it. They need to do their own bookkeeping basically. Chrome dispatches the event from the |
This seems fine to me by the way, but we should make sure to note this reason in File API as otherwise we might forget it later on. |
Remaining open questions:
|
My general bias would be that we avoid a future where a suite of tests for FileList needs to use a back door (e.g. DataTransferItemList) to test it. It seems a bit weird if you can get a mutable FileList or a FileList with multiple refs to the same file through some means, but we can't write isolated tests for that. But that runs counter to trying to make an ergonomic platform.
As an aside: we should be explicit (and test) that identity of the files is retained, i.e. |
I'm not sure I understand your first paragraph. I don't have a problem with supporting duplicate files here. Let's just go with that if that's what we already do. |
Sorry, mostly just arguing with myself and not getting anywhere. |
Multiple references to the same file should be possible.
An array should should meet requirement. There should not be a limit on the number of One additional implementation feature could be the ability to pass a single |
We should follow the other collections on the platform, such as Set, and accept an iterable of Files. I don't think anything else is reasonable; in particular, there is no precedent on the web platform for some of the more exotic suggestions in recent comments. |
Let me be more specific; this is a collection class, and our precedents must be other collection classes. (Blob is not a collection class.) |
With behavior like that I'd expect it to be |
Actually I was wrong, @Kaiido - that returns a new array rather than modifying existing ones - so it could work even with an immutable container. |
@bsitler Good for me.
|
Might it be worth polyfilling ideas for the new API first on top of the extant (at least in Blink) API to get a feeling for the most ergonomic and platform-native approach? For instance, here's a polyfilled shallow-only FileList pseudoconstructor: (() => {
try {
return void(new FileList(new File([''], '')));
} catch(e) {
}
const _real_FileList = FileList;
FileList = function(...items) {
const dataTransfer = new DataTransfer;
for (let item of items) dataTransfer.items.add(item);
return dataTransfer.files;
};
FileList.prototype = _real_FileList.prototype;
})(); edit: and with that polyfill, you can do e.g. new FileList(
new File(['a'],'a.txt'),
...new FileList(new File(['b'], 'b.txt'), new File(['c'], 'c.txt')),
new File(['d'], 'd.txt')); to flatten one filelist into another. Is it too cumbersome? |
@domenic so |
@bsittler If we include |
@annevk yep. |
@pwnall if |
FYI, I checked in the latest browsers. |
@inexorabletash outside of Chrome is it |
@annevk Have no experience with |
At blink is a new |
@annevk It'd be interesting to know how other browsers deal with the issue of repainting the input when a FileList vended by DataTransfer is updated. I'll write some code and get results on Monday, unless someone beats me to it. Given that Chrome doesn't follow If this doesn't work out, the other lever I see is having the HTMLInput.files setter do a copy of the FileList that is given to it. This seems slightly more wasteful, as DataTransfer can get away with creating a single FileLists when the files property is read after all the files are added (which I think is normal usage?). The approach also seems a bit worse for the future than having an immutable FileList, as any new API that takes in FileList will have to worry about its mutability. I think it's still better than having the repainting issue, though. |
@pwnall Firefox seems to follow However, their young implementation of settable |
FWIW, the code below returns same result at Chromium 62, Firefox 57 (have not tried at Safari, Edge) save for
|
@Kaiido Do you suggest that we compose the appropriate Does |
@guest271314 Thank you for the research! I think that Firefox's bug confirms my hunch that the input repaint issue is non-obvious and that we should try to find a solution that doesn't involve having a way to change the input's value indirectly (by mutating a FileList). |
@pwnall The code at previous comment was one of the approaches tried as a proof of concept for programmatically setting arbitrary The previous attempt did set arbitrary
If we could set Did not consider concepts "mutability" or "repaint" when attempting code to meet requirement. Given that the initial requirement is possible, from your perspective, what is the exact requirement now relevant to defining how the procedure of setting arbitrary |
@guest271314 Sorry for being unclear earlier! I didn't mean to imply there was anything wrong with your code! I am grateful that you looked into today's implementations! Given that (IIRC) we can't remove FileList from the Web platform, I think that the best outcome would be: // setup
const file1 = new File(...);
const file2 = new File(...);
const list = new FileList([file1, file2]);
fileInput.files = list;
fileInput.files === list; // true
const dt = new DataTransfer();
dt.items.add(file1);
const dt_list1 = dt.files;
dt.items.add(file2);
const dt_list2 = dt.files;
dt_list1 === dt.list2; // false Summary: FileList is immutable, HTMLInputElement.files acts like a vanilla JS property. A worse situation (IMO) that requires fewer browser changes: // setup
const file1 = new File(...);
const file2 = new File(...);
const list = new FileList();
list.push(file1);
list.push(file2);
fileInput.files = list;
fileInput.files.length === 2; // true
fileInput.files.item(0) === file1; // true
fileInput.files.item(1) === file2; // true
fileInput.files === list; // false
const dt = new DataTransfer();
dt.items.add(file1);
const dt_list1 = dt.files;
dt.items.add(file2);
const dt_list2 = dt.files;
dt_list1 === dt.list2; // true Summary: FileList is mutable, and the HTMLInputElement.files setter makes a copy of its argument. Does this answer your question? |
@annevk The question above also made me figure out one more wrinkle in the mutable FileList alternatives -- what happens to a FileList vended by a DataTransfer instance after the DataTransfer isn't |
@pwnall While we are here why cannot we rewrite the entire code including all dependencies to result in consistency between the API's as what what exactly |
Thanks all, sounds like it should be immutable and we'll need to update the drag & drop API to replace the to be returned instance upon mutation instead (and remove |
I looked into this briefly and at seems that as of yet, https://searchfox.org/mozilla-central/source/dom/webidl/DataTransfer.webidl and https://source.chromium.org/chromium/chromium/src/+/master:third_party/blink/renderer/core/clipboard/data_transfer.idl have |
Not sure to understand this last comment, @annevk could you clarify a bit how this is a blocker? From my understanding, browsers just follow the specs here, the I fail to see how the fact that this |
I thought the latest state of affairs in this was issue was #3269 (comment). HTML is not the place to introduce a |
Ok, from #3269 (comment) I didn't get as far as "HTML is not the place to introduce a Regarding what concerns HTML, I believe you can make it stop mutate the FileList by removing the As demonstrated by #3269 (comment) this would
The fact Chromium hasn't a bug against this interop issue makes me believe it wouldn't be a breaking change, but it's true WPT and implementers support are required. |
Could this be achieved instead by replacing Do we expect this to be impossible due to compat concerns? |
The likely compat issue is the (Even then, there could be compat problems of various sorts. But they'd be less likely...) |
Since recent changes about
HTMLInputElement.prototype.files
, it is possible to set this property to an other FileList object.Currently the only ways to get an FileList object are from an HTMLInputElement and a DataTransfer object.
Previously, these objects could not be created in a mutable state from scripts, but since the implementation of the DataTransfer constructor, it is now possible to create a mutable DataTransfer object.
This means that a script can currently create arbitrary FileList, containing script generated Files objects, and, by extension, that a script can set arbitrary Files in
HTMLInputElement.prototype.files
.See this fiddle in Blink which are currently the only ones to implement the DataTransfer constructor.
If this behavior is intended, it might be a good idea to implement a real API allowing this:
However, I am not sure about any security considerations there should be in such cases (e.g possibility to set a generated File with the same description as a File provided by the user).
Original discovery made by guest271314.
The text was updated successfully, but these errors were encountered: