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