_WKAttachment.mm   [plain text]


/*
 * Copyright (C) 2017 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 "_WKAttachment.h"

#import "APIAttachment.h"
#import "WKErrorPrivate.h"
#import "_WKAttachmentInternal.h"
#import <WebCore/MIMETypeRegistry.h>
#import <WebCore/SharedBuffer.h>
#import <wtf/BlockPtr.h>

#if PLATFORM(IOS_FAMILY)
#import <MobileCoreServices/MobileCoreServices.h>
#endif

static const NSInteger UnspecifiedAttachmentErrorCode = 1;
static const NSInteger InvalidAttachmentErrorCode = 2;

@implementation _WKAttachmentDisplayOptions : NSObject
@end

@implementation _WKAttachmentInfo {
    RetainPtr<NSFileWrapper> _fileWrapper;
    RetainPtr<NSString> _mimeType;
    RetainPtr<NSString> _utiType;
    RetainPtr<NSString> _filePath;
}

- (instancetype)initWithFileWrapper:(NSFileWrapper *)fileWrapper filePath:(NSString *)filePath mimeType:(NSString *)mimeType utiType:(NSString *)utiType
{
    if (!(self = [super init]))
        return nil;

    _fileWrapper = fileWrapper;
    _filePath = filePath;
    _mimeType = mimeType;
    _utiType = utiType;
    return self;
}

- (NSData *)data
{
    if (![_fileWrapper isRegularFile]) {
        // FIXME: Handle attachments backed by NSFileWrappers that represent directories.
        return nil;
    }

    return [_fileWrapper regularFileContents];
}

- (NSString *)name
{
    if ([_fileWrapper filename].length)
        return [_fileWrapper filename];

    return [_fileWrapper preferredFilename];
}

- (NSString *)filePath
{
    return _filePath.get();
}

- (NSFileWrapper *)fileWrapper
{
    return _fileWrapper.get();
}

- (NSString *)contentType
{
    if ([_mimeType length])
        return _mimeType.get();

    return _utiType.get();
}

@end

@implementation _WKAttachment

- (void)dealloc
{
    _attachment->~Attachment();

    [super dealloc];
}

- (API::Object&)_apiObject
{
    return *_attachment;
}

- (_WKAttachmentInfo *)info
{
    if (!_attachment->isValid())
        return nil;

    return [[[_WKAttachmentInfo alloc] initWithFileWrapper:_attachment->fileWrapper() filePath:_attachment->filePath() mimeType:_attachment->mimeType() utiType:_attachment->utiType()] autorelease];
}

- (void)requestInfo:(void(^)(_WKAttachmentInfo *, NSError *))completionHandler
{
    completionHandler(self.info, nil);
}

- (void)setFileWrapper:(NSFileWrapper *)fileWrapper contentType:(NSString *)contentType completion:(void (^)(NSError *))completionHandler
{
    if (!_attachment->isValid()) {
        if (completionHandler)
            completionHandler([NSError errorWithDomain:WKErrorDomain code:InvalidAttachmentErrorCode userInfo:nil]);
        return;
    }

    // This file path member is only populated when the attachment is generated upon dropping files. When data is specified via NSFileWrapper
    // from the SPI client, the corresponding file path of the data is unknown, if it even exists at all.
    _attachment->setFilePath({ });
    _attachment->setFileWrapperAndUpdateContentType(fileWrapper, contentType);
    _attachment->updateAttributes([capturedBlock = makeBlockPtr(completionHandler)] (auto error) {
        if (!capturedBlock)
            return;

        if (error == WebKit::CallbackBase::Error::None)
            capturedBlock(nil);
        else
            capturedBlock([NSError errorWithDomain:WKErrorDomain code:UnspecifiedAttachmentErrorCode userInfo:nil]);
    });
}

- (void)setData:(NSData *)data newContentType:(NSString *)newContentType newFilename:(NSString *)newFilename completion:(void(^)(NSError *))completionHandler
{
    auto fileWrapper = adoptNS([[NSFileWrapper alloc] initRegularFileWithContents:data]);
    if (newFilename)
        [fileWrapper setPreferredFilename:newFilename];
    [self setFileWrapper:fileWrapper.get() contentType:newContentType completion:completionHandler];
}

- (NSString *)uniqueIdentifier
{
    return _attachment->identifier();
}

- (NSString *)description
{
    return [NSString stringWithFormat:@"<%@ %p id='%@'>", [self class], self, self.uniqueIdentifier];
}

- (BOOL)isConnected
{
    return _attachment->insertionState() == API::Attachment::InsertionState::Inserted;
}

@end