Bug 58401

Summary: Enable folder drag-n-drop when using a "webkitdirectory" file input
Product: WebKit Reporter: John Gregg <johnnyg>
Component: FormsAssignee: John Gregg <johnnyg>
Status: RESOLVED FIXED    
Severity: Normal CC: adele, ap, commit-queue, levin
Priority: P2    
Version: 528+ (Nightly build)   
Hardware: Other   
OS: OS X 10.5   
Attachments:
Description Flags
Patch
none
Patch
none
Patch
none
Patch none

Description John Gregg 2011-04-12 17:19:50 PDT
Enable folder drag-n-drop when using a "webkitdirectory" file input
Comment 1 John Gregg 2011-04-12 17:37:52 PDT
Created attachment 89318 [details]
Patch
Comment 2 Alexey Proskuryakov 2011-04-12 22:50:10 PDT
This is confusing. I can drag and drop folders onto webkitdirectory inputs perfectly fine in Safari 5. What does this patch enable?

+    virtual void enumerateDirectory(const WTF::String&, PassRefPtr<WebCore::FileChooser>);

This almost certainly shouldn't be a PassRefPtr.
Comment 3 Darin Fisher (:fishd, Google) 2011-04-12 23:16:52 PDT
Comment on attachment 89318 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=89318&action=review

> Source/WebKit/chromium/public/WebViewClient.h:175
> +    virtual bool enumerateDirectory(const WebString& path,

enumerateChosenDirectory might be a better name here.  you want to convey the fact
that only chosen directories may be enumerated, right?  any reason not to name it
like this?

this function shouldn't live in the "editing" section of WebViewClient.  it might be
nice to group it with runFileChooser, since the two functions are related.  however,
that lives in a section for "dialogs" :(  maybe put this in the "misc" section nearby
the queryIconForFiles method?
Comment 4 John Gregg 2011-04-13 10:14:37 PDT
Alexey: You can always drag a directory onto the input, but what happens is that you get a single File with path = that directory.  On a webkitdirectory input in Chromium, where DIRECTORY_UPLOAD is enabled, that's inconsistent with what happens when you select the directory through the selection dialog, where the directory is enumerated and you get a list of all the Files with the webkitRelativePath attribute.  This patch makes drag-n-drop consistent with selecting a directory through the dialog, by doing the equivalent enumeration.

> +    virtual void enumerateDirectory(const WTF::String&, PassRefPtr<WebCore::FileChooser>);
> This almost certainly shouldn't be a PassRefPtr.

Can you clarify?  It's the same pattern as runOpenPanel().

Darin: I will rearrange.
Comment 5 John Gregg 2011-04-13 10:40:19 PDT
Created attachment 89400 [details]
Patch
Comment 6 Alexey Proskuryakov 2011-04-13 10:57:54 PDT
(In reply to comment #4)
> Alexey: You can always drag a directory onto the input, but what happens is that you get a single File with path = that directory.

That's a requirement of HTML5. "Unless the multiple attribute is set, there must be no more than one file in the list of selected files."

Was webkitdirectory discussed on appropriate mailing lists? There are many ways one could upload a directory, for example, it can be archived as a .zip file. Is there a spec for webkitdirectory that I could read to figure out what the idea behind it is?

> > +    virtual void enumerateDirectory(const WTF::String&, PassRefPtr<WebCore::FileChooser>);
> > This almost certainly shouldn't be a PassRefPtr.
> 
> Can you clarify?  It's the same pattern as runOpenPanel().

PassRefPtr should only be used when passing ownership, and neither enumerateDirectory() nor runOpenPanel() pass FileChooser ownership. See e.g. RenderFileUploadControl::click() - it passes m_fileChooser, obviously keeping ownership for itself.

So, runOpenPanel() should be changed to take a raw pointer (or a reference if the argument can't be null).
Comment 7 John Gregg 2011-04-13 11:12:16 PDT
(In reply to comment #6)
> (In reply to comment #4)
> > Alexey: You can always drag a directory onto the input, but what happens is that you get a single File with path = that directory.
> 
> That's a requirement of HTML5. "Unless the multiple attribute is set, there must be no more than one file in the list of selected files."
> 
> Was webkitdirectory discussed on appropriate mailing lists? There are many ways one could upload a directory, for example, it can be archived as a .zip file. Is there a spec for webkitdirectory that I could read to figure out what the idea behind it is?

It was discussed at some length about a year ago -- indeed there were many options proposed, but the   conclusion was to proceed with an experimental implementation.  There is no official spec, but the behavior is as described in the original proposal.  Regarding the semantics of multiple, I would think "directory" (aka "webkitdirectory") implies "multiple".

[http://lists.whatwg.org/htdig.cgi/whatwg-whatwg.org/2010-April/025764.html]

> 
> > > +    virtual void enumerateDirectory(const WTF::String&, PassRefPtr<WebCore::FileChooser>);
> > > This almost certainly shouldn't be a PassRefPtr.
> > 
> > Can you clarify?  It's the same pattern as runOpenPanel().
> 
> PassRefPtr should only be used when passing ownership, and neither enumerateDirectory() nor runOpenPanel() pass FileChooser ownership. See e.g. RenderFileUploadControl::click() - it passes m_fileChooser, obviously keeping ownership for itself.
> 
> So, runOpenPanel() should be changed to take a raw pointer (or a reference if the argument can't be null).

Makes sense.  I will address this in the patch.
Comment 8 Alexey Proskuryakov 2011-04-13 11:51:14 PDT
Thanks, I've read through the e-mail thread now.

This is not the right place to discuss it, but since there is no other discussion going on anywhere at the moment, I'll dump my concerns here.

With this proposal, there will be separate "multiple" and "directory" file input modifications, and they will be limited in different ways. Users will be able to upload multiple arbitrary files with the first variety, and they'll only be able to upload a single full directory with the second. I don't see how users could possibly ever learn the difference, or what the use case for allowing only the latter is.

Well, there is a use case for only uploading full directories, and that's OS X document bundles, which look like normal files to users. Safari already handles that case by creating a .zip archive on upload, so there is no need for new attributes for that.
Comment 9 John Gregg 2011-04-13 12:16:52 PDT
(In reply to comment #8)
> Thanks, I've read through the e-mail thread now.
> 
> This is not the right place to discuss it, but since there is no other discussion going on anywhere at the moment, I'll dump my concerns here.

I'll respond to your concerns, but I agree it would be more appropriate to address the concerns elsewhere, perhaps in the context of evaluating the experiment for something spec'd and permanent.  My goal with this bug is to fix a clear deficiency of the experimental feature.

> 
> With this proposal, there will be separate "multiple" and "directory" file input modifications, and they will be limited in different ways. Users will be able to upload multiple arbitrary files with the first variety, and they'll only be able to upload a single full directory with the second. I don't see how users could possibly ever learn the difference, or what the use case for allowing only the latter is.

This is true, it is up to the web application to use the right control and present it in a clear way.  That's a limitation of file inputs and the traditional UI that browsers use to present them (the multiple attribute is also not apparent to the user in Safari Chrome Firefox).  I agree with the comments on the original thread that better UI will help, such as an input that accepts anything dragged on it and has buttons for selecting and/or appending anything through dialogs.  One outcome could be that the webkitdirectory work is eventually rolled into that, but that is a larger undertaking.

> 
> Well, there is a use case for only uploading full directories, and that's OS X document bundles, which look like normal files to users. Safari already handles that case by creating a .zip archive on upload, so there is no need for new attributes for that.

The premise of this feature is that there are use cases for having a input with the semantics of representing a full directory.  Using a .zip file does not achieve that -- it has the semantics of a single file, and particularly doesn't expose the hierarchy to the web application.
Comment 10 Alexey Proskuryakov 2011-04-13 13:06:16 PDT
> My goal with this bug is to fix a clear deficiency of the experimental feature.

Given that the code in this patch is all under an #if, I don't have an objection to it.

Do you have a plan for the feature to either come out of experimental stage, or be dropped? With the information I have, I would consider dropping it soon - with the UI complications that it has, there doesn't seem to much chance of it being enabled by default or in many ports.
Comment 11 John Gregg 2011-04-13 13:53:56 PDT
Returning to a more immediate question, I took another look at the RefPtr usage and I think the use of PassRefPtr is preferable here.  

The enumeration is asynchronous, so in Chromium we put the FileChooser into a completion object (both here and in runOpenPanel) and we want to guarantee that FileChooser remains alive until completion, using a RefPtr<FileChooser>.  Putting a raw pointer in that completion object appears to risk a use-after-free bug if the RenderFileUploadControl is destroyed when the file chooser finishes.

Most other ports do runOpenPanel in a synchronous fashion -- WebKit2 is async, and puts the raw pointer in a completion object, maybe that's safe -- but the point is it's very hard to tell by code inspection.  It seems better to use refcounting in this case (given what FileChooser::disconnectClient() does to allow files to come back after the RenderFileUploadControl goes away).
Comment 12 Alexey Proskuryakov 2011-04-13 14:18:11 PDT
I disagree. The implementation may or may not temporarily protect the pointer, but this has nothing to do with the idea of taking ownership, or with the performance optimization from PassRefPtr.

It's certainly fine to use RefPtr in the completion object, but that doesn't mean that a function argument can be a PassRefPtr.
Comment 13 Alexey Proskuryakov 2011-04-13 14:32:27 PDT
Having PassRefPtr as an argument is very dangerous, and a common source of mistakes - see e.g. bug 58383 that was fixed today.
Comment 14 John Gregg 2011-04-13 18:18:01 PDT
Created attachment 89511 [details]
Patch
Comment 15 John Gregg 2011-04-15 09:38:11 PDT
(In reply to comment #13)
> Having PassRefPtr as an argument is very dangerous, and a common source of mistakes - see e.g. bug 58383 that was fixed today.

Okay, I'm fine with using your approach here.  Patch updated to not use PassRefPtr.
Comment 16 Darin Fisher (:fishd, Google) 2011-04-17 15:24:58 PDT
Comment on attachment 89511 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=89511&action=review

> Source/WebCore/rendering/RenderFileUploadControl.cpp:117
> +    return;

gratuitous return statement.
Comment 17 John Gregg 2011-04-19 01:10:54 PDT
Created attachment 90163 [details]
Patch
Comment 18 John Gregg 2011-04-19 01:12:02 PDT
Comment on attachment 90163 [details]
Patch

same patch with extra return; removed.
Comment 19 WebKit Commit Bot 2011-04-19 03:08:08 PDT
Comment on attachment 90163 [details]
Patch

Clearing flags on attachment: 90163

Committed r84238: <http://trac.webkit.org/changeset/84238>
Comment 20 WebKit Commit Bot 2011-04-19 04:01:48 PDT
The commit-queue encountered the following flaky tests while processing attachment 90163 [details]:

fast/canvas/webgl/gl-teximage.html bug 58766 (authors: kbr@google.com, mihaip@chromium.org, and zmo@google.com)
fast/dom/onerror-img.html bug 51019
The commit-queue is continuing to process your patch.
Comment 21 David Levin 2011-06-20 13:26:29 PDT
Comment on attachment 89511 [details]
Patch

A attachment obsoleted this one.
Comment 22 David Levin 2011-06-20 14:36:09 PDT
Patch was committed in #19.