Skip to content

Commit

Permalink
Camera RAW files picked via file input are returned as PNG on change
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=258467
rdar://111231838

Reviewed by Cameron McCormack.

Camera RAW type identifiers are registered with the system via the RawCamera
bundle. While the list of supported RAW types is fairly comprehensive, many
type identifiers are missing an associated MIME type.

Consequently, when choosing a RAW file (such as NEF) for an input that supports
RAW files and PNGs, the lack of correct information in the RawCamera bundle
results in ".nef" getting mapped to a nil MIME type.

WebKit also contains logic to automatically transcode images selected in file
inputs to an accepted MIME type, if the current MIME type is unsupported, and
the file is an image type. For file inputs that specify `accept` values in the
form of extensions, the extensions are normalized to MIME types.

For an input that supports RAW files and PNGs, WebKit attempts to transcode
the image, as "image/png" is the only recognized MIME type, following the system
lookup. Transcoding then succeeds, since ImageIO knows about the RAW type, and
is able to convert it to a PNG.

To fix, workaround the missing MIME type mappings in RawCamera.bundle, by
explicitly declaring them in WebKit.

* LayoutTests/TestExpectations:
* LayoutTests/fast/forms/file/entries-api/png-raw-open-panel-expected.txt: Added.
* LayoutTests/fast/forms/file/entries-api/png-raw-open-panel.html: Added.
* LayoutTests/fast/forms/file/entries-api/resources/images/image.nef: Added.
* LayoutTests/platform/mac-wk2/TestExpectations:
* Source/WebCore/platform/cocoa/MIMETypeRegistryCocoa.mm:
(WebCore::additionalMimeTypesMap):
(WebCore::additionalExtensionsMap):
(WebCore::MIMETypeRegistry::mimeTypeForExtension):
(WebCore::MIMETypeRegistry::extensionsForMIMEType):
(WebCore::MIMETypeRegistry::preferredExtensionForMIMEType):

Canonical link: https://commits.webkit.org/266049@main
  • Loading branch information
pxlcoder committed Jul 13, 2023
1 parent c7ca298 commit 00c3f53
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 3 deletions.
1 change: 1 addition & 0 deletions LayoutTests/TestExpectations
Original file line number Diff line number Diff line change
Expand Up @@ -1242,6 +1242,7 @@ fast/forms/file/entries-api/image-transcode-open-panel.html [ Skip ]
fast/forms/file/entries-api/image-no-transcode-open-panel.html [ Skip ]
fast/forms/file/entries-api/pages-jpeg-open-panel.html [ Skip ]
fast/forms/file/entries-api/pdf-jpeg-open-panel.html [ Skip ]
fast/forms/file/entries-api/png-raw-open-panel.html [ Skip ]

# This test is currently only relevant on mac-wk2
fast/canvas/webgl/context-update-on-display-configuration.html [ Skip ]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
OPEN FILE PANEL
Tests that a camera RAW file, chosen via the file picker which accepts RAW files and PNG images, does not transcode the file to PNG.

On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".


PASS fileList.length is 1
PASS file.name is "image.nef"
PASS file.type is "image/x-nikon-nef"
PASS file.size is 1955729
PASS successfullyParsed is true

TEST COMPLETE

36 changes: 36 additions & 0 deletions LayoutTests/fast/forms/file/entries-api/png-raw-open-panel.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<!DOCTYPE html>
<html>
<head>
<script src="../../../../resources/js-test.js"></script>
<script src="../../../../resources/ui-helper.js"></script>
</head>
<body>
<input id="input" type="file" accept=".png, .nef">
</body>
<script>

jsTestIsAsync = true;

addEventListener("load", async () => {
description("Tests that a camera RAW file, chosen via the file picker which accepts RAW files and PNG images, does not transcode the file to PNG.");

if (window.testRunner)
testRunner.setOpenPanelFiles(['resources/images/image.nef']);

input.addEventListener("change", (event) => {
fileList = event.target.files;
shouldBe("fileList.length", "1");

file = fileList[0];
shouldBeEqualToString("file.name", "image.nef");
shouldBeEqualToString("file.type", "image/x-nikon-nef");
shouldBe("file.size", "1955729");

finishJSTest();
});

UIHelper.activateElement(input);
});

</script>
</html>
Binary file not shown.
1 change: 1 addition & 0 deletions LayoutTests/platform/mac-wk2/TestExpectations
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ fast/animation/request-animation-frame-in-two-pages.html [ Pass ]
fast/forms/file/entries-api/image-transcode-open-panel.html [ Pass ]
fast/forms/file/entries-api/pages-jpeg-open-panel.html [ Pass ]
fast/forms/file/entries-api/pdf-jpeg-open-panel.html [ Pass ]
fast/forms/file/entries-api/png-raw-open-panel.html [ Pass ]

storage/indexeddb/database-transaction-cycle.html [ Pass ]

Expand Down
79 changes: 76 additions & 3 deletions Source/WebCore/platform/cocoa/MIMETypeRegistryCocoa.mm
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,53 @@
return extensionsForMIMETypeMap;
}

// Specify MIME type <-> extension mappings for type identifiers recognized by the system that are missing MIME type values.
static const HashMap<String, String, ASCIICaseInsensitiveHash>& additionalMimeTypesMap()
{
static NeverDestroyed<HashMap<String, String, ASCIICaseInsensitiveHash>> mimeTypesMap = [] {
HashMap<String, String, ASCIICaseInsensitiveHash> map;
static constexpr TypeExtensionPair additionalTypes[] = {
// FIXME: Remove this list once rdar://112044000 (Many camera RAW image type identifiers are missing MIME types) is resolved.
{ "image/x-canon-cr2"_s, "cr2"_s },
{ "image/x-canon-cr3"_s, "cr3"_s },
{ "image/x-epson-erf"_s, "erf"_s },
{ "image/x-fuji-raf"_s, "raf"_s },
{ "image/x-hasselblad-3fr"_s, "3fr"_s },
{ "image/x-hasselblad-fff"_s, "fff"_s },
{ "image/x-leaf-mos"_s, "mos"_s },
{ "image/x-leica-rwl"_s, "rwl"_s },
{ "image/x-minolta-mrw"_s, "mrw"_s },
{ "image/x-nikon-nef"_s, "nef"_s },
{ "image/x-olympus-orf"_s, "orf"_s },
{ "image/x-panasonic-raw"_s, "raw"_s },
{ "image/x-panasonic-rw2"_s, "rw2"_s },
{ "image/x-pentax-pef"_s, "pef"_s },
{ "image/x-phaseone-iiq"_s, "iiq"_s },
{ "image/x-samsung-srw"_s, "srw"_s },
{ "image/x-sony-arw"_s, "arw"_s },
{ "image/x-sony-srf"_s, "srf"_s },
};
for (auto& [type, extension] : additionalTypes)
map.add(extension, type);
return map;
}();
return mimeTypesMap;
}

static const HashMap<String, Vector<String>, ASCIICaseInsensitiveHash>& additionalExtensionsMap()
{
static NeverDestroyed<HashMap<String, Vector<String>, ASCIICaseInsensitiveHash>> extensionsMap = [] {
HashMap<String, Vector<String>, ASCIICaseInsensitiveHash> map;
for (auto& [extension, type] : additionalMimeTypesMap()) {
map.ensure(type, [] {
return Vector<String>();
}).iterator->value.append(extension);
}
return map;
}();
return extensionsMap;
}

static Vector<String> extensionsForWildcardMIMEType(const String& type)
{
Vector<String> extensions;
Expand All @@ -92,14 +139,32 @@
String MIMETypeRegistry::mimeTypeForExtension(StringView extension)
{
auto string = extension.createNSStringWithoutCopying();
return [[NSURLFileTypeMappings sharedMappings] MIMETypeForExtension:string.get()];

NSString *mimeType = [[NSURLFileTypeMappings sharedMappings] MIMETypeForExtension:string.get()];
if (mimeType.length)
return mimeType;

auto mapEntry = additionalMimeTypesMap().find<ASCIICaseInsensitiveStringViewHashTranslator>(extension);
if (mapEntry != additionalMimeTypesMap().end())
return mapEntry->value;

return nullString();
}

Vector<String> MIMETypeRegistry::extensionsForMIMEType(const String& type)
{
if (type.endsWith('*'))
return extensionsForWildcardMIMEType(type);
return makeVector<String>([[NSURLFileTypeMappings sharedMappings] extensionsForMIMEType:type]);

NSArray *extensions = [[NSURLFileTypeMappings sharedMappings] extensionsForMIMEType:type];
if (extensions.count)
return makeVector<String>(extensions);

auto mapEntry = additionalExtensionsMap().find(type);
if (mapEntry != additionalExtensionsMap().end())
return mapEntry->value;

return { };
}

String MIMETypeRegistry::preferredExtensionForMIMEType(const String& type)
Expand All @@ -109,7 +174,15 @@
if (isUSDMIMEType(type))
return "usdz"_s;

return [[NSURLFileTypeMappings sharedMappings] preferredExtensionForMIMEType:(NSString *)type];
NSString *preferredExtension = [[NSURLFileTypeMappings sharedMappings] preferredExtensionForMIMEType:(NSString *)type];
if (preferredExtension.length)
return preferredExtension;

auto mapEntry = additionalExtensionsMap().find(type);
if (mapEntry != additionalExtensionsMap().end())
return mapEntry->value.first();

return nullString();
}

bool MIMETypeRegistry::isApplicationPluginMIMEType(const String& MIMEType)
Expand Down

0 comments on commit 00c3f53

Please sign in to comment.