QuickLookThumbnailLoader.mm   [plain text]

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

#if HAVE(QUICKLOOK_THUMBNAILING)

#import <wtf/FileSystem.h>

#import "QuickLookThumbnailingSoftLink.h"

@implementation WKQLThumbnailQueueManager

- (instancetype)init
{
self = [super init];
if (self)
_queue = [[NSOperationQueue alloc] init];
return self;
}

- (void)dealloc
{
[_queue release];
[super dealloc];
}

+ (WKQLThumbnailQueueManager *)sharedInstance
{
static WKQLThumbnailQueueManager *sharedInstance = [[WKQLThumbnailQueueManager alloc] init];
return sharedInstance;
}

@end

@interface WKQLThumbnailLoadOperation ()
@property (atomic, readwrite, getter=isExecuting) BOOL executing;
@property (atomic, readwrite, getter=isFinished) BOOL finished;
@end

@implementation WKQLThumbnailLoadOperation {
RetainPtr<NSURL> _filePath;
RetainPtr<NSString> _identifier;
RetainPtr<NSFileWrapper> _fileWrapper;
RetainPtr<CocoaImage> _thumbnail;
BOOL _shouldWrite;
}

- (instancetype)initWithAttachment:(NSFileWrapper *)fileWrapper identifier:(NSString *)identifier
{
if (self = [super init]) {
_fileWrapper = fileWrapper;
_identifier = adoptNS([identifier copy]);
_shouldWrite = YES;
}
return self;
}

- (instancetype)initWithURL:(NSString *)fileURL identifier:(NSString *)identifier
{
if (self = [super init]) {
_identifier = adoptNS([identifier copy]);
_filePath = [NSURL fileURLWithPath:fileURL];
}
return self;
}

- (void)start
{
self.executing = YES;

if (_shouldWrite) {
NSString *temporaryDirectory = FileSystem::createTemporaryDirectory(@"QLTempFileData");

NSString *filePath = [temporaryDirectory stringByAppendingPathComponent:[_fileWrapper preferredFilename]];
NSFileWrapperWritingOptions options = 0;
NSError *error = nil;

auto fileURLPath = [NSURL fileURLWithPath:filePath];

[_fileWrapper writeToURL:fileURLPath options:options originalContentsURL:nil error:&error];
_filePath = WTFMove(fileURLPath);
if (error)
return;
}

auto request = adoptNS([WebKit::allocQLThumbnailGenerationRequestInstance() initWithFileAtURL:_filePath.get() size:CGSizeMake(400, 400) scale:1 representationTypes:QLThumbnailGenerationRequestRepresentationTypeThumbnail]);
[request setIconMode:YES];

[[WebKit::getQLThumbnailGeneratorClass() sharedGenerator] generateBestRepresentationForRequest:request.get() completionHandler:^(QLThumbnailRepresentation *thumbnail, NSError *error) {
if (error)
return;
if (_thumbnail)
return;
#if USE(APPKIT)
_thumbnail = thumbnail.NSImage;
#else
_thumbnail = thumbnail.UIImage;
#endif
if (_shouldWrite)
[[NSFileManager defaultManager] removeItemAtURL:_filePath.get() error:nullptr];

self.executing = NO;
self.finished = YES;
}];
}

- (CocoaImage *)thumbnail
{
return _thumbnail.get();
}

- (NSString *)identifier
{
return _identifier.get();
}

- (BOOL)isAsynchronous
{
return YES;
}

@synthesize executing = _executing;

- (BOOL)isExecuting
{
@synchronized(self) {
return _executing;
}
}

- (void)setExecuting:(BOOL)executing
{
@synchronized(self) {
if (executing != _executing) {
[self willChangeValueForKey:@"isExecuting"];
_executing = executing;
[self didChangeValueForKey:@"isExecuting"];
}
}
}

@synthesize finished = _finished;

- (BOOL)isFinished
{
@synchronized(self) {
return _finished;
}
}

- (void)setFinished:(BOOL)finished
{
@synchronized(self) {
if (finished != _finished) {
[self willChangeValueForKey:@"isFinished"];
_finished = finished;
[self didChangeValueForKey:@"isFinished"];
}
}
}

@end

#endif // HAVE(QUICKLOOK_THUMBNAILING)