whatwg/html

Specify cancelling a file upload from a file input

adroitwhiz opened this issue · 20 comments

When you activate a file input, most (all?) browsers open a file chooser dialog of some sort that allows you to choose a file, or cancel the operation.

However, the specification appears to completely ignore the latter option:

Run these steps in parallel:

  1. Optionally, wait until any prior execution of this algorithm has terminated.

  2. Display a prompt to the user requesting that the user specify some files. If the multiple attribute is not set, there must be no more than one file selected; otherwise, any number may be selected. Files can be from the filesystem or created on the fly, e.g., a picture taken from a camera connected to the user's device.

  3. Wait for the user to have made their selection.

  4. Update the file selection for the input element.

(Side note: "Run these steps in parallel... Wait" is a bit of a contradiction, no?)

Anyway, the language there seems to assume that the user will have made a selection, which may not be the case if the "cancel" button is pressed.

There seems to be at least one difference between browsers as a result of this--in Firefox and WebKit (via the Midori browser), cancelling a file select dialog will retain the previous state of the input and not emit any events, whereas in Chromium, it will change the state of the input to "no files selected" and emit a change event. These were the results on Linux--they may be different on other platforms.

As a result of this, there is currently no cross-browser way to tell if a user has clicked "Cancel" on a file upload dialog. Such a feature would be useful, for instance, if you want to put an "upload file" option in a menu that closes after you upload a file or cancel the dialog.

It would be useful to clarify the intended behavior here. If the intended behavior when canceling/closing the file selection dialog is to empty the file selection and count that as a change, then Firefox and WebKit should change. If the intended behavior is to retain the previous input and not emit a change event, then perhaps a cancel event could be added so that file dialogs opened via JavaScript would always resolve rather than leaving the caller hanging, so to speak?

In parallel effectively creates a new thread. Waiting there is fine. (At least in spec language.)

I think per the current language Firefox and Safari ought to change as canceling is making a selection, essentially.

cc @whatwg/forms

I disagree that canceling is making a selection. I would expect the file picker to retain the previously selected files. But I haven't done a usability study.

Is Chrome's behavior deliberate?

Ah, I misread, I would not expect the selection to change either in that case, but I could still see it being considered a selection.

Note the new File System Access API has well-specified behavior for this case

If the user dismissed the prompt without making a selection, reject p with an AbortError [sic] and abort.

Also web developers are apparently using fun hacks to try to detect this.

So I think we should specify this in more detail. However, given the different API surface without a clear error delivery channel, I'm not sure which behavior we should pick. /cc @mkruisselbrink, especially since Chrome is the odd one out.

https://crbug.com/1100116 was a request for an explicit cancel event of some sorts for input type=file. Not sure if that would be weird or not. I would tend to agree that cancelling not changing the selection seems more intuitive. Not sure if there is any historical reason for Chrome's current behavior.

From the last couple of mentions of this issue, people still seem to be running into this--I'm not sure who else to solicit opinions from to obtain some consensus and move this forward.

I don't have any opinion on whether canceling should be considered making a selection, but if it is decided that it doesn't count, then I definitely think that a cancel event should be added soon after (or along with) the relevant spec change, just so that web developers have some way of telling that it occurred.

@tomayac Since you ran into this a couple times, do you have an opinion on this?

I've added agenda+ to this issue as it seems like the folks who have been attending the triage meetings (#6551) might be able to help with it. /cc @smaug---- @mfreed7 in case they want to read up in advance.

@tomayac Since you ran into this a couple times, do you have an opinion on this?

FWIW, we have undone our previous hack due to incompatibility issues with Firefox (it worked in WebKit/Chromium-based browsers). We now approximate the behavior we would get from a proper cancelation event or an AbortError, which seems to work well enough as a workaround, but would definitely appreciate a clean way to handle this situation.

Coming back to this after the triage meeting. Even if canceling would clear an existing selection and fire the change event, that wouldn't help when there is no existing selection. As is the case when you wrap <input type=file>.click() in a function. So it seems that even though that behavior difference exists when there is an existing selection, you need something like a cancel (or abort?) signal either way.

It does seem somewhat reasonable to offer such a signal, though the signal should not be used when canceling alters the selection, as can be the case in Chrome today.

I wonder if we offer this signal, if we should restrict it to first parties (same origin with top-level origin) as a first step toward making this dialog harder to use from a third party context.

Aside: usability of this control leaves a lot to be desired. In Firefox/Safari you cannot clear your selection. In Chrome you can only clear it through clicking "Choose File" first.

Somewhat related: I realized that Safari makes <a download> cancelable, whereas all other browsers immediately perform the download. Whatever mechanism we come up with for the present issue should probably also work for <a download>.

Screen Shot 2021-05-07 at 15 24 07

though the signal should not be used when canceling alters the selection, as can be the case in Chrome today.

Why?

Whatever mechanism we come up with for the present issue should probably also work for <a download>.

That seems like pretty dangerous scope creep to me, which is likely to derail this idea :(.

@domenic would you expect both abort and a change event in some order? What if a browser offered a "clear selection" button? It seems to me that if pressing cancel actually changes the selection it's not an abort, but a change.

Getting the notification reminded me to ask this:

I wonder if we offer this signal, if we should restrict it to first parties (same origin with top-level origin) as a first step toward making this dialog harder to use from a third party context.

What's the rationale behind making the dialog harder to use from a third party context?

Also, was a decision reached as to whether cancelling should clear the selection or not, or if it's fine with that being browser-specific?

What's the rationale behind making the dialog harder to use from a third party context?

We don't want third parties to be able to show dialogs generally (it might be okay if the first party grants it that ability through Permissions Policy).

No decisions were reached (we don't make decisions during meetings).

@domenic would you expect both abort and a change event in some order?

Yeah, that's what I would expect.

What if a browser offered a "clear selection" button?

That would be only a change event. Effectively, Chrome's UX combines both of those into the cancel button.

It seems to me that if pressing cancel actually changes the selection it's not an abort, but a change.

It's both, IMO.

We don't want third parties to be able to show dialogs generally (it might be okay if the first party grants it that ability through a Permissions Policy).

In that case, I don't think making the API surface less ergonomic in third-party contexts would help matters. I doubt the types of people who want to do malicious/shady things by opening dialogs would be deterred by bad ergonomics, but legitimate developers would definitely suffer.

If we offer Permissions Policy legitimate developers would end up adopting that and then we could block it for the other cases.

I agree that Permissions Policy would be the way to go--I'm saying that making the cancel/abort event only usable from first-party contexts reduces ergonomics without providing any increase in security. I'm completely fine with blocking the ability to open dialogs in the first place.

It's both, IMO.

To me, at least, abort would indicate a lack of change--e.g. the user was going to change their selection but instead chose not to, and thus abort and change seem mutually exclusive to me.

I've put up a pull request at #6735; thoughts appreciated.