blob: 697fc6fe881063897bf69703e8417883d98bf730 [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/base/platform_mime_util.h"
#import <Foundation/Foundation.h>
#import <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
#include <string>
#include "base/mac/foundation_util.h"
#include "base/mac/scoped_cftyperef.h"
#include "base/notreached.h"
#include "base/strings/sys_string_conversions.h"
#include "build/build_config.h"
#if BUILDFLAG(IS_IOS)
#include <MobileCoreServices/MobileCoreServices.h>
#else
#include <CoreServices/CoreServices.h>
#endif // BUILDFLAG(IS_IOS)
namespace net {
bool PlatformMimeUtil::GetPlatformMimeTypeFromExtension(
const base::FilePath::StringType& ext,
std::string* result) const {
std::string ext_nodot = ext;
if (ext_nodot.length() >= 1 && ext_nodot[0] == L'.') {
ext_nodot.erase(ext_nodot.begin());
}
// TODO(crbug.com/1227419): Remove iOS availability check when cronet
// deployment target is bumped to 14.
if (@available(macOS 11, iOS 14, *)) {
UTType* uttype =
[UTType typeWithFilenameExtension:base::SysUTF8ToNSString(ext_nodot)];
// Dynamic UTTypes are made by the system in the event that there's a
// non-identifiable mime type. For now, we should treat dynamic UTTypes as a
// nonstandard format.
if ([uttype isDynamic] || uttype.preferredMIMEType == nil) {
return false;
}
*result = base::SysNSStringToUTF8(uttype.preferredMIMEType);
return true;
}
#if (BUILDFLAG(IS_MAC) && \
MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_VERSION_11_0) || \
(BUILDFLAG(IS_IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_14_0)
else {
base::ScopedCFTypeRef<CFStringRef> ext_ref(
base::SysUTF8ToCFStringRef(ext_nodot));
if (!ext_ref) {
return false;
}
base::ScopedCFTypeRef<CFStringRef> uti(
UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension,
ext_ref, nullptr));
if (!uti) {
return false;
}
base::ScopedCFTypeRef<CFStringRef> mime_ref(
UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType));
if (!mime_ref) {
return false;
}
*result = base::SysCFStringRefToUTF8(mime_ref);
return true;
}
#else
NOTREACHED();
return false;
#endif // (BUILDFLAG(IS_MAC) && MAC_OS_X_VERSION_MIN_REQUIRED <
// MAC_OS_VERSION_11_0) || (BUILDFLAG(IS_IOS) &&
// __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_14_0)
}
bool PlatformMimeUtil::GetPlatformPreferredExtensionForMimeType(
const std::string& mime_type,
base::FilePath::StringType* ext) const {
// TODO(crbug.com/1227419): Remove iOS availability check when cronet
// deployment target is bumped to 14.
if (@available(macOS 11, iOS 14, *)) {
UTType* uttype =
[UTType typeWithMIMEType:base::SysUTF8ToNSString(mime_type)];
if ([uttype isDynamic] || uttype.preferredFilenameExtension == nil) {
return false;
}
*ext = base::SysNSStringToUTF8(uttype.preferredFilenameExtension);
return true;
}
#if (BUILDFLAG(IS_MAC) && \
MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_VERSION_11_0) || \
(BUILDFLAG(IS_IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_14_0)
else {
base::ScopedCFTypeRef<CFStringRef> mime_ref(
base::SysUTF8ToCFStringRef(mime_type));
if (!mime_ref) {
return false;
}
base::ScopedCFTypeRef<CFStringRef> uti(
UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mime_ref,
nullptr));
if (!uti) {
return false;
}
base::ScopedCFTypeRef<CFStringRef> ext_ref(
UTTypeCopyPreferredTagWithClass(uti, kUTTagClassFilenameExtension));
if (!ext_ref) {
return false;
}
*ext = base::SysCFStringRefToUTF8(ext_ref);
return true;
}
#else
NOTREACHED();
return false;
#endif // (BUILDFLAG(IS_MAC) && MAC_OS_X_VERSION_MIN_REQUIRED <
// MAC_OS_VERSION_11_0) || (BUILDFLAG(IS_IOS) &&
// __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_14_0)
}
void PlatformMimeUtil::GetPlatformExtensionsForMimeType(
const std::string& mime_type,
std::unordered_set<base::FilePath::StringType>* extensions) const {
// TODO(crbug.com/1227419): Remove iOS availability check when cronet
// deployment target is bumped to 14.
if (@available(macOS 11, iOS 14, *)) {
NSArray<UTType*>* types =
[UTType typesWithTag:base::SysUTF8ToNSString(mime_type)
tagClass:UTTagClassMIMEType
conformingToType:nil];
bool extensions_found = false;
if (types) {
NSInteger numberOfTypes = (NSInteger)types.count;
for (NSInteger i = 0; i < numberOfTypes; ++i) {
UTType* type = types[i];
if (!type || type.preferredFilenameExtension == nil) {
continue;
}
extensions_found = true;
NSArray<NSString*>* extensions_list =
type.tags[UTTagClassFilenameExtension];
for (NSString* extension in extensions_list) {
extensions->insert(base::SysNSStringToUTF8(extension));
}
}
}
if (extensions_found) {
return;
}
base::FilePath::StringType ext;
if (GetPlatformPreferredExtensionForMimeType(mime_type, &ext)) {
extensions->insert(ext);
}
}
#if (BUILDFLAG(IS_MAC) && \
MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_VERSION_11_0) || \
(BUILDFLAG(IS_IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_14_0)
else {
base::ScopedCFTypeRef<CFStringRef> mime_ref(
base::SysUTF8ToCFStringRef(mime_type));
if (mime_ref) {
bool extensions_found = false;
base::ScopedCFTypeRef<CFArrayRef> types(UTTypeCreateAllIdentifiersForTag(
kUTTagClassMIMEType, mime_ref, nullptr));
if (types) {
for (CFIndex i = 0; i < CFArrayGetCount(types); i++) {
base::ScopedCFTypeRef<CFArrayRef> extensions_list(
UTTypeCopyAllTagsWithClass(base::mac::CFCast<CFStringRef>(
CFArrayGetValueAtIndex(types, i)),
kUTTagClassFilenameExtension));
if (!extensions_list) {
continue;
}
extensions_found = true;
for (NSString* extension in base::mac::CFToNSCast(extensions_list)) {
extensions->insert(base::SysNSStringToUTF8(extension));
}
}
}
if (extensions_found) {
return;
}
}
// Huh? Give up.
base::FilePath::StringType ext;
if (GetPlatformPreferredExtensionForMimeType(mime_type, &ext)) {
extensions->insert(ext);
}
}
#endif // (BUILDFLAG(IS_MAC) && MAC_OS_X_VERSION_MIN_REQUIRED <
// MAC_OS_VERSION_11_0) || (BUILDFLAG(IS_IOS) &&
// __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_14_0)
}
} // namespace net