blob: a0c1416a2dcd5a091130ff9cf577c1688da73308 [file] [log] [blame]
/*
* Copyright (C) 2018 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "config.h"
#import "APIAttachment.h"
#import "PageClient.h"
#import <WebCore/MIMETypeRegistry.h>
#import <WebCore/SharedBuffer.h>
#if PLATFORM(IOS_FAMILY)
#import <MobileCoreServices/MobileCoreServices.h>
#else
#import <CoreServices/CoreServices.h>
#endif
#import <pal/spi/cocoa/NSKeyedArchiverSPI.h>
#define CAN_SECURELY_ARCHIVE_FILE_WRAPPER (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101400) || (PLATFORM(IOS_FAMILY) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 120000)
namespace API {
static WTF::String mimeTypeInferredFromFileExtension(const API::Attachment& attachment)
{
if (NSString *fileExtension = [(NSString *)attachment.fileName() pathExtension])
return WebCore::MIMETypeRegistry::getMIMETypeForExtension(fileExtension);
return { };
}
static BOOL isDeclaredOrDynamicTypeIdentifier(NSString *type)
{
return UTTypeIsDeclared((__bridge CFStringRef)type) || UTTypeIsDynamic((__bridge CFStringRef)type);
}
NSFileWrapper *Attachment::fileWrapper() const
{
if (m_fileWrapperGenerator && !m_fileWrapper)
m_fileWrapper = m_fileWrapperGenerator();
return m_fileWrapper.get();
}
void Attachment::invalidateGeneratedFileWrapper()
{
ASSERT(m_fileWrapperGenerator);
m_fileWrapper = nil;
m_webPage->didInvalidateDataForAttachment(*this);
}
WTF::String Attachment::mimeType() const
{
NSString *contentType = m_contentType.isEmpty() ? mimeTypeInferredFromFileExtension(*this) : m_contentType;
if (!contentType.length)
return nullString();
if (!isDeclaredOrDynamicTypeIdentifier(contentType))
return contentType;
return adoptCF(UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)contentType, kUTTagClassMIMEType)).get();
}
WTF::String Attachment::utiType() const
{
NSString *contentType = m_contentType.isEmpty() ? mimeTypeInferredFromFileExtension(*this) : m_contentType;
if (!contentType.length)
return nullString();
if (isDeclaredOrDynamicTypeIdentifier(contentType))
return contentType;
return adoptCF(UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, (__bridge CFStringRef)contentType, nullptr)).get();
}
WTF::String Attachment::fileName() const
{
auto fileWrapper = this->fileWrapper();
if ([fileWrapper filename].length)
return [fileWrapper filename];
return [fileWrapper preferredFilename];
}
void Attachment::setFileWrapperAndUpdateContentType(NSFileWrapper *fileWrapper, NSString *contentType)
{
if (!contentType.length) {
if (fileWrapper.directory)
contentType = (NSString *)kUTTypeDirectory;
else if (fileWrapper.regularFile) {
if (NSString *pathExtension = (fileWrapper.filename.length ? fileWrapper.filename : fileWrapper.preferredFilename).pathExtension)
contentType = WebCore::MIMETypeRegistry::getMIMETypeForExtension(pathExtension);
if (!contentType.length)
contentType = (NSString *)kUTTypeData;
}
}
setContentType(contentType);
setFileWrapper(fileWrapper);
}
Optional<uint64_t> Attachment::fileSizeForDisplay() const
{
auto fileWrapper = this->fileWrapper();
if (![fileWrapper isRegularFile]) {
// FIXME: We should display a size estimate for directory-type file wrappers.
return WTF::nullopt;
}
if (auto fileSize = [[fileWrapper fileAttributes][NSFileSize] unsignedLongLongValue])
return fileSize;
return [fileWrapper regularFileContents].length;
}
RefPtr<WebCore::SharedBuffer> Attachment::enclosingImageData() const
{
if (!m_hasEnclosingImage)
return nullptr;
auto fileWrapper = this->fileWrapper();
if (![fileWrapper isRegularFile])
return nullptr;
NSData *data = [fileWrapper regularFileContents];
if (!data)
return nullptr;
return WebCore::SharedBuffer::create(data);
}
bool Attachment::isEmpty() const
{
return !m_fileWrapper && !m_fileWrapperGenerator;
}
RefPtr<WebCore::SharedBuffer> Attachment::createSerializedRepresentation() const
{
auto fileWrapper = this->fileWrapper();
if (!fileWrapper || !m_webPage)
return nullptr;
#if CAN_SECURELY_ARCHIVE_FILE_WRAPPER
NSData *serializedData = securelyArchivedDataWithRootObject(fileWrapper);
#else
NSData *serializedData = insecurelyArchivedDataWithRootObject(fileWrapper);
#endif
if (!serializedData)
return nullptr;
return WebCore::SharedBuffer::create(serializedData);
}
void Attachment::updateFromSerializedRepresentation(Ref<WebCore::SharedBuffer>&& serializedRepresentation, const WTF::String& contentType)
{
if (!m_webPage)
return;
auto serializedData = serializedRepresentation->createNSData();
if (!serializedData)
return;
#if CAN_SECURELY_ARCHIVE_FILE_WRAPPER
NSFileWrapper *fileWrapper = unarchivedObjectOfClassesFromData(m_webPage->pageClient().serializableFileWrapperClasses(), serializedData.get());
#else
NSFileWrapper *fileWrapper = insecurelyUnarchiveObjectFromData(serializedData.get());
#endif
if (![fileWrapper isKindOfClass:NSFileWrapper.class])
return;
setFileWrapperAndUpdateContentType(fileWrapper, contentType);
m_webPage->updateAttachmentAttributes(*this, [] (auto) { });
}
void Attachment::setFileWrapperGenerator(Function<RetainPtr<NSFileWrapper>(void)>&& fileWrapperGenerator)
{
m_fileWrapperGenerator = WTFMove(fileWrapperGenerator);
m_fileWrapper = nil;
m_webPage->didInvalidateDataForAttachment(*this);
}
} // namespace API