diff --git a/Tests/LibWeb/TestMimeSniff.cpp b/Tests/LibWeb/TestMimeSniff.cpp index a4be94e66c..1bb1a0e324 100644 --- a/Tests/LibWeb/TestMimeSniff.cpp +++ b/Tests/LibWeb/TestMimeSniff.cpp @@ -50,6 +50,16 @@ static void set_image_type_mappings(HashMap>& mim mime_type_to_headers_map.set("image/jpeg"sv, { "\xFF\xD8\xFF"sv }); } +static void set_audio_or_video_type_mappings(HashMap>& mime_type_to_headers_map) +{ + mime_type_to_headers_map.set("audio/aiff"sv, { "FORM\x00\x00\x00\x00\x41IFF"sv }); + mime_type_to_headers_map.set("audio/mpeg"sv, { "ID3"sv }); + mime_type_to_headers_map.set("application/ogg"sv, { "OggS\x00"sv }); + mime_type_to_headers_map.set("audio/midi"sv, { "MThd\x00\x00\x00\x06"sv }); + mime_type_to_headers_map.set("video/avi"sv, { "RIFF\x00\x00\x00\x00\x41\x56\x49\x20"sv }); + mime_type_to_headers_map.set("audio/wave"sv, { "RIFF\x00\x00\x00\x00WAVE"sv }); +} + TEST_CASE(determine_computed_mime_type_in_both_none_and_browsing_sniffing_context) { HashMap> mime_type_to_headers_map; @@ -85,13 +95,8 @@ TEST_CASE(determine_computed_mime_type_in_both_none_and_browsing_sniffing_contex }); set_image_type_mappings(mime_type_to_headers_map); + set_audio_or_video_type_mappings(mime_type_to_headers_map); - mime_type_to_headers_map.set("audio/aiff"sv, { "FORM\x00\x00\x00\x00\x41IFF"sv }); - mime_type_to_headers_map.set("audio/mpeg"sv, { "ID3"sv }); - mime_type_to_headers_map.set("application/ogg"sv, { "OggS\x00"sv }); - mime_type_to_headers_map.set("audio/midi"sv, { "MThd\x00\x00\x00\x06"sv }); - mime_type_to_headers_map.set("video/avi"sv, { "RIFF\x00\x00\x00\x00\x41\x56\x49\x20"sv }); - mime_type_to_headers_map.set("audio/wave"sv, { "RIFF\x00\x00\x00\x00WAVE"sv }); mime_type_to_headers_map.set("application/x-gzip"sv, { "\x1F\x8B\x08"sv }); mime_type_to_headers_map.set("application/zip"sv, { "PK\x03\x04"sv }); mime_type_to_headers_map.set("application/x-rar-compressed"sv, { "Rar\x20\x1A\x07\x00"sv }); @@ -159,3 +164,41 @@ TEST_CASE(determine_computed_mime_type_in_image_sniffing_context) EXPECT_EQ(mime_type, computed_mime_type.essence()); } + +TEST_CASE(determine_computed_mime_type_in_audio_or_video_sniffing_context) +{ + // Cover case where supplied type is an XML MIME type. + auto mime_type = "application/rss+xml"sv; + auto supplied_type = MUST(Web::MimeSniff::MimeType::parse(mime_type)).release_value(); + auto computed_mime_type = MUST(Web::MimeSniff::Resource::sniff(""sv.bytes(), Web::MimeSniff::SniffingConfiguration { + .sniffing_context = Web::MimeSniff::SniffingContext::AudioOrVideo, + .supplied_type = supplied_type, + })); + + EXPECT_EQ(mime_type, MUST(computed_mime_type.serialized())); + HashMap> mime_type_to_headers_map; + + set_audio_or_video_type_mappings(mime_type_to_headers_map); + + // Also consider a resource that is not an audio or video. + mime_type_to_headers_map.set("application/octet-stream"sv, { "\x00"sv }); + + for (auto const& mime_type_to_headers : mime_type_to_headers_map) { + auto mime_type = mime_type_to_headers.key; + + for (auto const& header : mime_type_to_headers.value) { + auto computed_mime_type = MUST(Web::MimeSniff::Resource::sniff(header.bytes(), Web::MimeSniff::SniffingConfiguration { .sniffing_context = Web::MimeSniff::SniffingContext::AudioOrVideo })); + EXPECT_EQ(mime_type, computed_mime_type.essence()); + } + } + + // Cover case where we aren't dealing with an audio or video MIME type. + mime_type = "text/html"sv; + supplied_type = MUST(Web::MimeSniff::MimeType::parse("text/html"sv)).release_value(); + computed_mime_type = MUST(Web::MimeSniff::Resource::sniff(""sv.bytes(), Web::MimeSniff::SniffingConfiguration { + .sniffing_context = Web::MimeSniff::SniffingContext::AudioOrVideo, + .supplied_type = supplied_type, + })); + + EXPECT_EQ(mime_type, computed_mime_type.essence()); +} diff --git a/Userland/Libraries/LibWeb/MimeSniff/Resource.cpp b/Userland/Libraries/LibWeb/MimeSniff/Resource.cpp index 60e693ab90..d4fa09a626 100644 --- a/Userland/Libraries/LibWeb/MimeSniff/Resource.cpp +++ b/Userland/Libraries/LibWeb/MimeSniff/Resource.cpp @@ -540,6 +540,8 @@ ErrorOr Resource::context_specific_sniffing_algorithm(SniffingContext snif if (sniffing_context == SniffingContext::Image) return rules_for_sniffing_images_specifically(); + if (sniffing_context == SniffingContext::AudioOrVideo) + return rules_for_sniffing_audio_or_video_specifically(); return {}; } @@ -576,4 +578,36 @@ ErrorOr Resource::rules_for_sniffing_images_specifically() return {}; } +// https://mimesniff.spec.whatwg.org/#sniffing-in-an-audio-or-video-context +ErrorOr Resource::rules_for_sniffing_audio_or_video_specifically() +{ + // 1. If the supplied MIME type is an XML MIME type, the computed MIME type is the supplied MIME type. + // Abort these steps. + // NOTE: Non-standard but due to the mime type detection algorithm we need this sanity check. + if (m_supplied_mime_type.has_value() && m_supplied_mime_type->is_xml()) { + m_computed_mime_type = m_supplied_mime_type.value(); + return {}; + } + + // 2. Let audio-or-video-type-matched be the result of executing the audio or video type pattern matching + // algorithm with the resource header as the byte sequence to be matched. + auto audio_or_video_type_matched = TRY(match_an_audio_or_video_type_pattern(resource_header())); + + // 3. If audio-or-video-type-matched is not undefined, the computed MIME type is audio-or-video-type-matched. + // Abort these steps. + if (audio_or_video_type_matched.has_value()) { + m_computed_mime_type = audio_or_video_type_matched.release_value(); + return {}; + } + + // 4. The computed MIME type is the supplied MIME type. + // NOTE: Non-standard but due to the mime type detection algorithm we need this sanity check. + if (m_supplied_mime_type.has_value()) { + m_computed_mime_type = m_supplied_mime_type.value(); + } + + // NOTE: Non-standard but if the supplied mime type is undefined, we use computed mime type's default value. + return {}; +} + } diff --git a/Userland/Libraries/LibWeb/MimeSniff/Resource.h b/Userland/Libraries/LibWeb/MimeSniff/Resource.h index 826e49a4d8..17a89221f4 100644 --- a/Userland/Libraries/LibWeb/MimeSniff/Resource.h +++ b/Userland/Libraries/LibWeb/MimeSniff/Resource.h @@ -13,7 +13,8 @@ namespace Web::MimeSniff { enum class SniffingContext { None, Browsing, - Image + Image, + AudioOrVideo }; struct SniffingConfiguration { @@ -42,6 +43,7 @@ private: ErrorOr mime_type_sniffing_algorithm(); ErrorOr context_specific_sniffing_algorithm(SniffingContext sniffing_context); ErrorOr rules_for_sniffing_images_specifically(); + ErrorOr rules_for_sniffing_audio_or_video_specifically(); // https://mimesniff.spec.whatwg.org/#supplied-mime-type // A supplied MIME type, the MIME type determined by the supplied MIME type detection algorithm.