Bug 212482 - [macOS] Drag/drop an image of a unsupported format to an file input element should convert it to a supported format
Summary: [macOS] Drag/drop an image of a unsupported format to an file input element s...
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: Images (show other bugs)
Version: WebKit Nightly Build
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Said Abou-Hallawa
URL:
Keywords: InRadar
Depends on: 212489
Blocks:
  Show dependency treegraph
 
Reported: 2020-05-28 11:51 PDT by Said Abou-Hallawa
Modified: 2020-08-09 22:12 PDT (History)
11 users (show)

See Also:


Attachments
Patch (51.46 KB, patch)
2020-06-21 15:47 PDT, Said Abou-Hallawa
no flags Details | Formatted Diff | Diff
Patch (34.05 KB, patch)
2020-08-05 14:40 PDT, Said Abou-Hallawa
no flags Details | Formatted Diff | Diff
Patch (34.14 KB, patch)
2020-08-05 14:51 PDT, Said Abou-Hallawa
no flags Details | Formatted Diff | Diff
Patch (34.12 KB, patch)
2020-08-05 15:05 PDT, Said Abou-Hallawa
no flags Details | Formatted Diff | Diff
Patch (34.13 KB, patch)
2020-08-05 15:32 PDT, Said Abou-Hallawa
no flags Details | Formatted Diff | Diff
Patch (45.14 KB, patch)
2020-08-06 18:03 PDT, Said Abou-Hallawa
no flags Details | Formatted Diff | Diff
Patch (50.30 KB, patch)
2020-08-07 17:30 PDT, Said Abou-Hallawa
no flags Details | Formatted Diff | Diff
Patch (50.39 KB, patch)
2020-08-09 15:06 PDT, Said Abou-Hallawa
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Said Abou-Hallawa 2020-05-28 11:51:44 PDT
If a page has an <input> file element which accepts an image like one of these:

<input type="file" accept="image/*">
<input type="file" accept="image/png, image/jpeg">

And the user drags/drops an image of unsupported format, HEIF for example, the image should be converted to one of the formats in the "accept" attribute if possible. This will be similar to what happens when dragging and dropping a photo from the "Photos" app which is converting the HEIF image to JPEG image.
Comment 1 Radar WebKit Bug Importer 2020-05-28 13:56:10 PDT
<rdar://problem/63731672>
Comment 2 Said Abou-Hallawa 2020-06-21 15:47:52 PDT
Created attachment 402432 [details]
Patch
Comment 3 Said Abou-Hallawa 2020-08-05 14:40:50 PDT
Created attachment 406041 [details]
Patch

WIP patch
Comment 4 Said Abou-Hallawa 2020-08-05 14:42:31 PDT
This patch is mostly working. It still needs some clean-up and maybe threading.
Comment 5 Said Abou-Hallawa 2020-08-05 14:51:36 PDT
Created attachment 406044 [details]
Patch
Comment 6 Said Abou-Hallawa 2020-08-05 15:05:00 PDT
Created attachment 406047 [details]
Patch
Comment 7 Said Abou-Hallawa 2020-08-05 15:32:19 PDT
Created attachment 406051 [details]
Patch
Comment 8 Said Abou-Hallawa 2020-08-06 18:03:37 PDT
Created attachment 406141 [details]
Patch
Comment 9 Said Abou-Hallawa 2020-08-06 18:07:02 PDT
Layout test were added.
Comment 10 Said Abou-Hallawa 2020-08-07 17:30:12 PDT
Created attachment 406235 [details]
Patch
Comment 11 Darin Adler 2020-08-08 11:25:27 PDT
Comment on attachment 406235 [details]
Patch

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

> Source/WebCore/html/FileInputType.cpp:504
> +    sharedImageTranscodingQueue()->dispatch([this, protectedThis = makeRef(*this), paths = paths.isolatedCopy(), transcodingPaths = transcodingPaths.isolatedCopy(), transcodingUTI = transcodingUTI.isolatedCopy(), transcodingExtension = transcodingExtension.isolatedCopy()]() mutable {

Not 100% sure about the efficiency/thread-safety here.

With protectedThis seems like we are carefully passing this in a way that does not require a thread-safe reference count. Perhaps we could use the same approach with "paths" that we use with protectedThis, and not make an isolated copy, since it’s only going to be used back on the main thread. I think that means that here we would write "paths" and below we would write paths = WTFMove(paths) and it would be safe for the same reason that protectedThis is safe.

Alternatively we could change InputType to use ThreadSafeRefCounted, to make this more obviously thread safe.

Maybe another way to write this that’s more obviously safe would be to create another inner lambda, one that takes a replacementPaths argument, as a local variable and capture *that* in the the outer lambda. Then we would not need to capture "this", "protectedThis", or "paths" in the outer lambda:

    auto callFilesChosen = [protectedThis = makeRef(this), paths] (const Vector<String>& replacementPaths) {
        protectedThis->filesChosen(paths, replacementPaths);
    };
    sharedImageTranscodingQueue()->dispatch([callFilesChosen = WTFMove(callFilesChosen), transcodingPaths = transcodingPaths.isolatedCopy(), transcodingUTI = transcodingUTI.isolatedCopy(), transcodingExtension = transcodingExtension.isolatedCopy()]() mutable {
        ASSERT(!RunLoop::isMain());
        auto replacementPaths = transcodeImages(transcodingPaths, transcodingUTI, transcodingExtension);
        ASSERT(transcodingPaths.size() == replacementPaths.size());
        RunLoop::main().dispatch([callFilesChosen = WTFMove(callFilesChosen), replacementPaths = replacementPaths.isolatedCopy()]() {
            callFilesChosen(replacementPaths);
        });
    });

I think the thread safety is easier to understand in that version.

> Source/WebCore/platform/graphics/ImageUtilities.h:30
> +WEBCORE_EXPORT Ref<WorkQueue> sharedImageTranscodingQueue();

I suggest this return WorkQueue& instead of Ref<WorkQueue> since the queue in question is global and immortal. No reason to return a newly protected pointer each time.
Comment 12 Said Abou-Hallawa 2020-08-09 15:06:08 PDT
Created attachment 406273 [details]
Patch
Comment 13 Said Abou-Hallawa 2020-08-09 21:44:45 PDT
Comment on attachment 406235 [details]
Patch

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

>> Source/WebCore/html/FileInputType.cpp:504
>> +    sharedImageTranscodingQueue()->dispatch([this, protectedThis = makeRef(*this), paths = paths.isolatedCopy(), transcodingPaths = transcodingPaths.isolatedCopy(), transcodingUTI = transcodingUTI.isolatedCopy(), transcodingExtension = transcodingExtension.isolatedCopy()]() mutable {
> 
> Not 100% sure about the efficiency/thread-safety here.
> 
> With protectedThis seems like we are carefully passing this in a way that does not require a thread-safe reference count. Perhaps we could use the same approach with "paths" that we use with protectedThis, and not make an isolated copy, since it’s only going to be used back on the main thread. I think that means that here we would write "paths" and below we would write paths = WTFMove(paths) and it would be safe for the same reason that protectedThis is safe.
> 
> Alternatively we could change InputType to use ThreadSafeRefCounted, to make this more obviously thread safe.
> 
> Maybe another way to write this that’s more obviously safe would be to create another inner lambda, one that takes a replacementPaths argument, as a local variable and capture *that* in the the outer lambda. Then we would not need to capture "this", "protectedThis", or "paths" in the outer lambda:
> 
>     auto callFilesChosen = [protectedThis = makeRef(this), paths] (const Vector<String>& replacementPaths) {
>         protectedThis->filesChosen(paths, replacementPaths);
>     };
>     sharedImageTranscodingQueue()->dispatch([callFilesChosen = WTFMove(callFilesChosen), transcodingPaths = transcodingPaths.isolatedCopy(), transcodingUTI = transcodingUTI.isolatedCopy(), transcodingExtension = transcodingExtension.isolatedCopy()]() mutable {
>         ASSERT(!RunLoop::isMain());
>         auto replacementPaths = transcodeImages(transcodingPaths, transcodingUTI, transcodingExtension);
>         ASSERT(transcodingPaths.size() == replacementPaths.size());
>         RunLoop::main().dispatch([callFilesChosen = WTFMove(callFilesChosen), replacementPaths = replacementPaths.isolatedCopy()]() {
>             callFilesChosen(replacementPaths);
>         });
>     });
> 
> I think the thread safety is easier to understand in that version.

This is neat.

I have just realized that lambda captures its parameters when it's defined not when it is called. Using this solution, we do not have to capture *this" twice: (1) from the main thread to the transcoding thread and (2) form the transcoding thread back to the main thread.
Comment 14 EWS 2020-08-09 22:12:40 PDT
Committed r265422: <https://trac.webkit.org/changeset/265422>

All reviewed patches have been landed. Closing bug and clearing flags on attachment 406273 [details].