[iOS] NSFileManager and QuickLook SPI not available in public SDK
[WebKit-https.git] / Source / WebCore / platform / network / ios / QuickLook.mm
1 /*
2  * Copyright (C) 2009 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "QuickLook.h"
28
29 #if USE(QUICK_LOOK)
30
31 #import "DocumentLoader.h"
32 #import "FileSystemIOS.h"
33 #import "Logging.h"
34 #import "NSFileManagerSPI.h"
35 #import "QuickLookSPI.h"
36 #import "ResourceError.h"
37 #import "ResourceHandle.h"
38 #import "ResourceLoader.h"
39 #import "RuntimeApplicationChecksIOS.h"
40 #import "SoftLinking.h"
41 #import "SynchronousResourceHandleCFURLConnectionDelegate.h"
42 #import "WebCoreURLResponseIOS.h"
43 #import <Foundation/Foundation.h>
44 #import <wtf/NeverDestroyed.h>
45 #import <wtf/StdLibExtras.h>
46 #import <wtf/Threading.h>
47 #import <wtf/Vector.h>
48 #import <wtf/text/WTFString.h>
49
50 #if USE(CFNETWORK)
51 #import <CFNetwork/CFURLConnection.h>
52
53 @interface NSURLResponse (QuickLookDetails)
54 +(NSURLResponse *)_responseWithCFURLResponse:(CFURLResponseRef)response;
55 -(CFURLResponseRef)_CFURLResponse;
56 @end
57 #endif
58
59 SOFT_LINK_FRAMEWORK_OPTIONAL(QuickLook)
60 SOFT_LINK_CLASS(QuickLook, QLPreviewConverter)
61 SOFT_LINK_MAY_FAIL(QuickLook, QLPreviewGetSupportedMIMETypes, NSSet *, (), ())
62 SOFT_LINK_MAY_FAIL(QuickLook, QLTypeCopyBestMimeTypeForFileNameAndMimeType, NSString *, (NSString *fileName, NSString *mimeType), (fileName, mimeType))
63 SOFT_LINK_MAY_FAIL(QuickLook, QLTypeCopyBestMimeTypeForURLAndMimeType, NSString *, (NSURL *url, NSString *mimeType), (url, mimeType))
64 SOFT_LINK_MAY_FAIL(QuickLook, QLTypeCopyUTIForURLAndMimeType, NSString *, (NSURL *url, NSString *mimeType), (url, mimeType))
65 SOFT_LINK_CONSTANT_MAY_FAIL(QuickLook, QLPreviewScheme, NSString *)
66
67 namespace WebCore {
68     NSString *QLTypeCopyUTIForURLAndMimeType(NSURL *url, NSString *mimeType);
69 }
70
71 using namespace WebCore;
72
73 Class WebCore::QLPreviewConverterClass()
74 {
75 #define QLPreviewConverter getQLPreviewConverterClass()
76     return QLPreviewConverter;
77 #undef QLPreviewConverter
78 }
79
80 NSString *WebCore::QLTypeCopyBestMimeTypeForFileNameAndMimeType(NSString *fileName, NSString *mimeType)
81 {
82     if (!canLoadQLTypeCopyBestMimeTypeForFileNameAndMimeType())
83         return nil;
84
85     return ::QLTypeCopyBestMimeTypeForFileNameAndMimeType(fileName, mimeType);
86 }
87
88 NSString *WebCore::QLTypeCopyBestMimeTypeForURLAndMimeType(NSURL *url, NSString *mimeType)
89 {
90     if (!canLoadQLTypeCopyBestMimeTypeForURLAndMimeType())
91         return nil;
92
93     return ::QLTypeCopyBestMimeTypeForURLAndMimeType(url, mimeType);
94 }
95
96 NSSet *WebCore::QLPreviewGetSupportedMIMETypesSet()
97 {
98     if (!canLoadQLPreviewGetSupportedMIMETypes())
99         return nil;
100
101     static NSSet *set = adoptNS(::QLPreviewGetSupportedMIMETypes()).leakRef();
102     return set;
103 }
104
105 NSString *WebCore::QLTypeCopyUTIForURLAndMimeType(NSURL *url, NSString *mimeType)
106 {
107     if (!canLoadQLTypeCopyUTIForURLAndMimeType())
108         return nil;
109
110     return ::QLTypeCopyUTIForURLAndMimeType(url, mimeType);
111 }
112
113 NSDictionary *WebCore::QLFileAttributes()
114 {
115     // Set file perms to owner read/write only
116     NSNumber *filePOSIXPermissions = [NSNumber numberWithInteger:(WEB_UREAD | WEB_UWRITE)];
117     static NSDictionary *dictionary = adoptNS([[NSDictionary alloc] initWithObjectsAndKeys:
118                                         NSUserName(), NSFileOwnerAccountName,
119                                         filePOSIXPermissions, NSFilePosixPermissions,
120                                         nullptr]).leakRef();
121     return dictionary;
122 }
123
124 NSDictionary *WebCore::QLDirectoryAttributes()
125 {
126     // Set file perms to owner read/write/execute only
127     NSNumber *directoryPOSIXPermissions = [NSNumber numberWithInteger:(WEB_UREAD | WEB_UWRITE | WEB_UEXEC)];
128     static NSDictionary *dictionary = adoptNS([[NSDictionary alloc] initWithObjectsAndKeys:
129                                                 NSUserName(), NSFileOwnerAccountName,
130                                                 directoryPOSIXPermissions, NSFilePosixPermissions,
131                                                 nullptr]).leakRef();
132     return dictionary;
133 }
134
135 static Mutex& qlPreviewConverterDictionaryMutex()
136 {
137     static NeverDestroyed<Mutex> mutex;
138     return mutex;
139 }
140
141 static NSMutableDictionary *QLPreviewConverterDictionary()
142 {
143     static NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
144     return dictionary;
145 }
146
147 static NSMutableDictionary *QLContentDictionary()
148 {
149     static NSMutableDictionary *contentDictionary = [[NSMutableDictionary alloc] init];
150     return contentDictionary;
151 }
152
153 void WebCore::addQLPreviewConverterWithFileForURL(NSURL *url, id converter, NSString *fileName)
154 {
155     ASSERT(url);
156     ASSERT(converter);
157     MutexLocker lock(qlPreviewConverterDictionaryMutex());
158     [QLPreviewConverterDictionary() setObject:converter forKey:url];
159     [QLContentDictionary() setObject:(fileName ? fileName : @"") forKey:url];
160 }
161
162 NSString *WebCore::qlPreviewConverterFileNameForURL(NSURL *url)
163 {
164     return [QLContentDictionary() objectForKey:url];
165 }
166
167 NSString *WebCore::qlPreviewConverterUTIForURL(NSURL *url)
168 {
169     id converter = nil;
170     {
171         MutexLocker lock(qlPreviewConverterDictionaryMutex());
172         converter = [QLPreviewConverterDictionary() objectForKey:url];
173     }
174     if (!converter)
175         return nil;
176     return [converter previewUTI];
177 }
178
179 void WebCore::removeQLPreviewConverterForURL(NSURL *url)
180 {
181     MutexLocker lock(qlPreviewConverterDictionaryMutex());
182     [QLPreviewConverterDictionary() removeObjectForKey:url];
183
184     // Delete the file when we remove the preview converter
185     NSString *filename = qlPreviewConverterFileNameForURL(url);
186     if ([filename length])
187         [[NSFileManager defaultManager] _web_removeFileOnlyAtPath:filename];
188     [QLContentDictionary() removeObjectForKey:url];
189 }
190
191 PassOwnPtr<ResourceRequest> WebCore::registerQLPreviewConverterIfNeeded(NSURL *url, NSString *mimeType, NSData *data)
192 {
193     RetainPtr<NSString> updatedMIMEType = adoptNS(WebCore::QLTypeCopyBestMimeTypeForURLAndMimeType(url, mimeType));
194
195     if ([WebCore::QLPreviewGetSupportedMIMETypesSet() containsObject:updatedMIMEType.get()]) {
196         RetainPtr<NSString> uti = adoptNS(WebCore::QLTypeCopyUTIForURLAndMimeType(url, updatedMIMEType.get()));
197
198         RetainPtr<id> converter = adoptNS([[QLPreviewConverterClass() alloc] initWithData:data name:nil uti:uti.get() options:nil]);
199         NSURLRequest *request = [converter previewRequest];
200
201         // We use [request URL] here instead of url since it will be
202         // the URL that the WebDataSource will see during -dealloc.
203         addQLPreviewConverterWithFileForURL([request URL], converter.get(), nil);
204
205         return adoptPtr(new ResourceRequest(request));
206     }
207
208     return nullptr;
209 }
210
211 const URL WebCore::safeQLURLForDocumentURLAndResourceURL(const URL& documentURL, const String& resourceURL)
212 {
213     id converter = nil;
214     NSURL *nsDocumentURL = documentURL;
215     {
216         MutexLocker lock(qlPreviewConverterDictionaryMutex());
217         converter = [QLPreviewConverterDictionary() objectForKey:nsDocumentURL];
218     }
219
220     if (!converter)
221         return URL(ParsedURLString, resourceURL);
222
223     RetainPtr<NSURLRequest> request = adoptNS([[NSURLRequest alloc] initWithURL:[NSURL URLWithString:resourceURL]]);
224     NSURLRequest *safeRequest = [converter safeRequestForRequest:request.get()];
225     return [safeRequest URL];
226 }
227
228 static Vector<char> createQLPreviewProtocol()
229 {
230     Vector<char> previewProtocol;
231 #define QLPreviewScheme getQLPreviewScheme()
232     const char* qlPreviewScheme = [QLPreviewScheme UTF8String];
233 #undef QLPreviewScheme
234     previewProtocol.append(qlPreviewScheme, strlen(qlPreviewScheme) + 1);
235     return previewProtocol;
236 }
237
238 const char* WebCore::QLPreviewProtocol()
239 {
240     if (!canLoadQLPreviewScheme())
241         return "";
242
243     static NeverDestroyed<Vector<char>> previewProtocol(createQLPreviewProtocol());
244     return previewProtocol.get().data();
245 }
246
247 #if USE(CFNETWORK)
248 // The way QuickLook works is we pass it an NSURLConnectionDelegate callback object at creation
249 // time. Then we pass it all the data as we receive it. Once we've downloaded the full URL,
250 // QuickLook turns around and send us, through this delegate, the HTML version of the file which we
251 // pass on to WebCore. The flag m_finishedLoadingDataIntoConverter in QuickLookHandle decides
252 // whether to pass the data to QuickLook or WebCore.
253 //
254 // This works fine when using NS APIs, but when using CFNetwork, we don't have a NSURLConnectionDelegate.
255 // So we create WebQuickLookHandleAsDelegate as an intermediate delegate object and pass it to
256 // QLPreviewConverter. The proxy delegate then forwards the messages on to the CFNetwork code.
257 @interface WebQuickLookHandleAsDelegate : NSObject <NSURLConnectionDelegate> {
258     RefPtr<SynchronousResourceHandleCFURLConnectionDelegate> m_connectionDelegate;
259 }
260
261 - (id)initWithConnectionDelegate:(SynchronousResourceHandleCFURLConnectionDelegate*)connectionDelegate;
262 - (void)clearHandle;
263 @end
264
265 @implementation WebQuickLookHandleAsDelegate
266 - (id)initWithConnectionDelegate:(SynchronousResourceHandleCFURLConnectionDelegate*)connectionDelegate
267 {
268     self = [super init];
269     if (!self)
270         return nil;
271     m_connectionDelegate = connectionDelegate;
272     return self;
273 }
274
275 - (void)connection:(NSURLConnection *)connection didReceiveDataArray:(NSArray *)dataArray
276 {
277     UNUSED_PARAM(connection);
278     if (!m_connectionDelegate)
279         return;
280     LOG(Network, "WebQuickLookHandleAsDelegate::didReceiveDataArray()");
281     m_connectionDelegate->didReceiveDataArray(reinterpret_cast<CFArrayRef>(dataArray));
282 }
283
284 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data lengthReceived:(long long)lengthReceived
285 {
286     UNUSED_PARAM(connection);
287     if (!m_connectionDelegate)
288         return;
289     LOG(Network, "WebQuickLookHandleAsDelegate::didReceiveData() - data length = %ld", (long)[data length]);
290
291     // QuickLook code sends us a nil data at times. The check below is the same as the one in
292     // ResourceHandleMac.cpp added for a different bug.
293     if (![data length])
294         return;
295     m_connectionDelegate->didReceiveData(reinterpret_cast<CFDataRef>(data), static_cast<int>(lengthReceived));
296 }
297
298 - (void)connectionDidFinishLoading:(NSURLConnection *)connection
299 {
300     UNUSED_PARAM(connection);
301     if (!m_connectionDelegate)
302         return;
303     LOG(Network, "WebQuickLookHandleAsDelegate::didFinishLoading()");
304     m_connectionDelegate->didFinishLoading();
305 }
306
307 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
308 {
309     UNUSED_PARAM(connection);
310     if (!m_connectionDelegate)
311         return;
312     LOG(Network, "WebQuickLookHandleAsDelegate::didFail()");
313     m_connectionDelegate->didFail(reinterpret_cast<CFErrorRef>(error));
314 }
315
316 - (void)clearHandle
317 {
318     m_connectionDelegate = nullptr;
319 }
320 @end
321 #endif
322
323 @interface WebResourceLoaderQuickLookDelegate : NSObject <NSURLConnectionDelegate> {
324     RefPtr<ResourceLoader> _resourceLoader;
325     BOOL _hasSentDidReceiveResponse;
326     BOOL _hasFailed;
327 }
328 @property (nonatomic) QuickLookHandle* quickLookHandle;
329 @end
330
331 @implementation WebResourceLoaderQuickLookDelegate
332
333 - (id)initWithResourceLoader:(PassRefPtr<ResourceLoader>)resourceLoader
334 {
335     self = [super init];
336     if (!self)
337         return nil;
338
339     _resourceLoader = resourceLoader;
340     return self;
341 }
342
343 - (void)_sendDidReceiveResponseIfNecessary
344 {
345     if (_hasSentDidReceiveResponse || _hasFailed || !_quickLookHandle)
346         return;
347
348     // QuickLook might fail to convert a document without calling connection:didFailWithError: (see <rdar://problem/17927972>).
349     // A nil MIME type is an indication of such a failure, so stop loading the resource and ignore subsequent delegate messages.
350     NSURLResponse *previewResponse = _quickLookHandle->nsResponse();
351     if (![previewResponse MIMEType]) {
352         _hasFailed = YES;
353         _resourceLoader->didFail(_resourceLoader->cannotShowURLError());
354         return;
355     }
356
357     _hasSentDidReceiveResponse = YES;
358     _resourceLoader->didReceiveResponse(previewResponse);
359 }
360
361 #if USE(NETWORK_CFDATA_ARRAY_CALLBACK)
362 - (void)connection:(NSURLConnection *)connection didReceiveDataArray:(NSArray *)dataArray
363 {
364     UNUSED_PARAM(connection);
365     if (!_resourceLoader)
366         return;
367
368     [self _sendDidReceiveResponseIfNecessary];
369     if (_hasFailed)
370         return;
371
372     _resourceLoader->didReceiveDataArray(reinterpret_cast<CFArrayRef>(dataArray));
373 }
374 #endif
375
376 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data lengthReceived:(long long)lengthReceived
377 {
378     UNUSED_PARAM(connection);
379     if (!_resourceLoader)
380         return;
381
382     [self _sendDidReceiveResponseIfNecessary];
383     if (_hasFailed)
384         return;
385
386     // QuickLook code sends us a nil data at times. The check below is the same as the one in
387     // ResourceHandleMac.cpp added for a different bug.
388     if (![data length])
389         return;
390     _resourceLoader->didReceiveData(reinterpret_cast<const char*>([data bytes]), [data length], lengthReceived, DataPayloadBytes);
391 }
392
393 - (void)connectionDidFinishLoading:(NSURLConnection *)connection
394 {
395     UNUSED_PARAM(connection);
396     if (!_resourceLoader || _hasFailed)
397         return;
398
399     ASSERT(_hasSentDidReceiveResponse);
400     _resourceLoader->didFinishLoading(0);
401 }
402
403 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
404 {
405     UNUSED_PARAM(connection);
406
407     [self _sendDidReceiveResponseIfNecessary];
408     if (_hasFailed)
409         return;
410
411     _resourceLoader->didFail(ResourceError(error));
412 }
413
414 - (void)clearHandle
415 {
416     _resourceLoader = nullptr;
417     _quickLookHandle = nullptr;
418 }
419
420 @end
421
422 namespace WebCore {
423
424 NSString *createTemporaryFileForQuickLook(NSString *fileName)
425 {
426     NSString *downloadDirectory = createTemporaryDirectory(@"QuickLookContent");
427     if (!downloadDirectory)
428         return nil;
429
430     NSString *contentPath = [downloadDirectory stringByAppendingPathComponent:fileName];
431     NSFileManager *fileManager = [NSFileManager defaultManager];
432     NSString *uniqueContentPath = [fileManager _web_pathWithUniqueFilenameForPath:contentPath];
433
434     BOOL success = [fileManager _web_createFileAtPathWithIntermediateDirectories:uniqueContentPath
435                                                                         contents:nil
436                                                                       attributes:QLFileAttributes()
437                                                              directoryAttributes:QLDirectoryAttributes()];
438
439     return success ? uniqueContentPath : nil;
440 }
441
442 static inline QuickLookHandleClient* emptyClient()
443 {
444     static NeverDestroyed<QuickLookHandleClient> emptyClient;
445     return &emptyClient.get();
446 }
447
448 QuickLookHandle::QuickLookHandle(NSURL *firstRequestURL, NSURLConnection *connection, NSURLResponse *nsResponse, id delegate)
449     : m_firstRequestURL(firstRequestURL)
450     , m_converter(adoptNS([[QLPreviewConverterClass() alloc] initWithConnection:connection delegate:delegate response:nsResponse options:nil]))
451     , m_delegate(delegate)
452     , m_finishedLoadingDataIntoConverter(false)
453     , m_nsResponse([m_converter previewResponse])
454     , m_client(emptyClient())
455 {
456     LOG(Network, "QuickLookHandle::QuickLookHandle() - previewFileName: %s", [m_converter previewFileName]);
457 }
458
459 std::unique_ptr<QuickLookHandle> QuickLookHandle::create(ResourceHandle* handle, NSURLConnection *connection, NSURLResponse *nsResponse, id delegate)
460 {
461     ASSERT_ARG(handle, handle);
462     if (!handle->firstRequest().deprecatedIsMainResourceRequest() || ![WebCore::QLPreviewGetSupportedMIMETypesSet() containsObject:[nsResponse MIMEType]])
463         return nullptr;
464
465     std::unique_ptr<QuickLookHandle> quickLookHandle(new QuickLookHandle([handle->firstRequest().nsURLRequest(DoNotUpdateHTTPBody) URL], connection, nsResponse, delegate));
466     handle->client()->didCreateQuickLookHandle(*quickLookHandle);
467     return WTF::move(quickLookHandle);
468 }
469
470 #if USE(CFNETWORK)
471 std::unique_ptr<QuickLookHandle> QuickLookHandle::create(ResourceHandle* handle, SynchronousResourceHandleCFURLConnectionDelegate* connectionDelegate, CFURLResponseRef cfResponse)
472 {
473     ASSERT_ARG(handle, handle);
474     if (!handle->firstRequest().deprecatedIsMainResourceRequest() || ![WebCore::QLPreviewGetSupportedMIMETypesSet() containsObject:(NSString *)CFURLResponseGetMIMEType(cfResponse)])
475         return nullptr;
476
477     NSURLResponse *nsResponse = [NSURLResponse _responseWithCFURLResponse:cfResponse];
478     WebQuickLookHandleAsDelegate *delegate = [[[WebQuickLookHandleAsDelegate alloc] initWithConnectionDelegate:connectionDelegate] autorelease];
479     std::unique_ptr<QuickLookHandle> quickLookHandle(new QuickLookHandle([handle->firstRequest().nsURLRequest(DoNotUpdateHTTPBody) URL], nil, nsResponse, delegate));
480     handle->client()->didCreateQuickLookHandle(*quickLookHandle);
481     return WTF::move(quickLookHandle);
482 }
483
484 CFURLResponseRef QuickLookHandle::cfResponse()
485 {
486     return [m_nsResponse _CFURLResponse];
487 }
488 #endif
489
490 bool QuickLookHandle::shouldCreateForMIMEType(const String& mimeType)
491 {
492     return [WebCore::QLPreviewGetSupportedMIMETypesSet() containsObject:mimeType];
493 }
494
495 std::unique_ptr<QuickLookHandle> QuickLookHandle::create(ResourceLoader& loader, const ResourceResponse& response)
496 {
497     ASSERT(shouldCreateForMIMEType(response.mimeType()));
498
499     RetainPtr<WebResourceLoaderQuickLookDelegate> delegate = adoptNS([[WebResourceLoaderQuickLookDelegate alloc] initWithResourceLoader:&loader]);
500     std::unique_ptr<QuickLookHandle> quickLookHandle(new QuickLookHandle([loader.originalRequest().nsURLRequest(DoNotUpdateHTTPBody) URL], nil, response.nsURLResponse(), delegate.get()));
501     [delegate setQuickLookHandle:quickLookHandle.get()];
502     loader.didCreateQuickLookHandle(*quickLookHandle);
503     return WTF::move(quickLookHandle);
504 }
505
506 NSURLResponse *QuickLookHandle::nsResponse()
507 {
508     return m_nsResponse.get();
509 }
510
511 bool QuickLookHandle::didReceiveDataArray(CFArrayRef cfDataArray)
512 {
513     if (m_finishedLoadingDataIntoConverter)
514         return false;
515
516     LOG(Network, "QuickLookHandle::didReceiveDataArray()");
517     [m_converter appendDataArray:(NSArray *)cfDataArray];
518     m_client->didReceiveDataArray(cfDataArray);
519     return true;
520 }
521
522 bool QuickLookHandle::didReceiveData(CFDataRef cfData)
523 {
524     if (m_finishedLoadingDataIntoConverter)
525         return false;
526     
527     return didReceiveDataArray(adoptCF(CFArrayCreate(kCFAllocatorDefault, (const void**)&cfData, 1, &kCFTypeArrayCallBacks)).get());
528 }
529
530 bool QuickLookHandle::didFinishLoading()
531 {
532     if (m_finishedLoadingDataIntoConverter)
533         return false;
534
535     LOG(Network, "QuickLookHandle::didFinishLoading()");
536     m_finishedLoadingDataIntoConverter = YES;
537     [m_converter finishedAppendingData];
538     m_client->didFinishLoading();
539     return true;
540 }
541
542 void QuickLookHandle::didFail()
543 {
544     LOG(Network, "QuickLookHandle::didFail()");
545     m_client->didFail();
546     [m_converter finishConverting];
547     m_converter = nullptr;
548 }
549
550 QuickLookHandle::~QuickLookHandle()
551 {
552     LOG(Network, "QuickLookHandle::~QuickLookHandle()");
553     m_converter = nullptr;
554
555     [m_delegate clearHandle];
556 }
557
558 String QuickLookHandle::previewFileName() const
559 {
560     return [m_converter previewFileName];
561 }
562
563 String QuickLookHandle::previewUTI() const
564 {
565     return [m_converter previewUTI];
566 }
567
568 NSURL *QuickLookHandle::previewRequestURL() const
569 {
570     return [[m_converter previewRequest] URL];
571 }
572
573 }
574
575 #endif // USE(QUICK_LOOK)