2 * Copyright (C) 2009-2017 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #import "PreviewLoader.h"
31 #import "DocumentLoader.h"
32 #import "FrameLoader.h"
33 #import "FrameLoaderClient.h"
35 #import "PreviewConverter.h"
36 #import "PreviewLoaderClient.h"
38 #import "QuickLookSPI.h"
39 #import "ResourceLoader.h"
40 #import <wtf/NeverDestroyed.h>
42 using namespace WebCore;
44 @interface WebPreviewLoader : NSObject {
45 RefPtr<ResourceLoader> _resourceLoader;
46 ResourceResponse _response;
47 RefPtr<PreviewLoaderClient> _client;
48 std::unique_ptr<PreviewConverter> _converter;
49 RetainPtr<NSMutableArray> _bufferedDataArray;
50 BOOL _hasSentDidReceiveResponse;
53 - (instancetype)initWithResourceLoader:(ResourceLoader&)resourceLoader resourceResponse:(const ResourceResponse&)resourceResponse;
54 - (void)appendDataArray:(NSArray<NSData *> *)dataArray;
55 - (void)finishedAppending;
60 @implementation WebPreviewLoader
62 static RefPtr<PreviewLoaderClient>& testingClient()
64 static NeverDestroyed<RefPtr<PreviewLoaderClient>> testingClient;
65 return testingClient.get();
68 static PreviewLoaderClient& emptyClient()
70 static NeverDestroyed<PreviewLoaderClient> emptyClient;
71 return emptyClient.get();
74 - (instancetype)initWithResourceLoader:(ResourceLoader&)resourceLoader resourceResponse:(const ResourceResponse&)resourceResponse
80 _resourceLoader = &resourceLoader;
81 _response = resourceResponse;
82 _converter = std::make_unique<PreviewConverter>(self, _response);
83 _bufferedDataArray = adoptNS([[NSMutableArray alloc] init]);
86 _client = testingClient();
87 else if (auto client = resourceLoader.frameLoader()->client().createPreviewLoaderClient(_converter->previewFileName(), _converter->previewUTI()))
88 _client = WTFMove(client);
90 _client = &emptyClient();
92 LOG(Network, "WebPreviewConverter created with preview file name \"%s\".", _converter->previewFileName().utf8().data());
96 - (void)appendDataArray:(NSArray<NSData *> *)dataArray
98 LOG(Network, "WebPreviewConverter appending data array with count %ld.", dataArray.count);
99 [_converter->platformConverter() appendDataArray:dataArray];
100 [_bufferedDataArray addObjectsFromArray:dataArray];
101 _client->didReceiveDataArray((CFArrayRef)dataArray);
104 - (void)finishedAppending
106 LOG(Network, "WebPreviewConverter finished appending data.");
107 [_converter->platformConverter() finishedAppendingData];
108 _client->didFinishLoading();
113 LOG(Network, "WebPreviewConverter failed.");
114 [_converter->platformConverter() finishConverting];
118 - (void)_sendDidReceiveResponseIfNecessary
120 ASSERT(!_resourceLoader->reachedTerminalState());
121 if (_hasSentDidReceiveResponse)
124 [_bufferedDataArray removeAllObjects];
126 ResourceResponse response { _converter->previewResponse() };
127 response.setIsQuickLook(true);
128 ASSERT(response.mimeType().length());
130 _resourceLoader->documentLoader()->setPreviewConverter(WTFMove(_converter));
132 _hasSentDidReceiveResponse = YES;
133 _resourceLoader->didReceiveResponse(response);
136 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data lengthReceived:(long long)lengthReceived
138 ASSERT_UNUSED(connection, !connection);
139 if (_resourceLoader->reachedTerminalState())
142 [self _sendDidReceiveResponseIfNecessary];
144 // QuickLook code sends us a nil data at times. The check below is the same as the one in
145 // ResourceHandleMac.cpp added for a different bug.
146 if (auto dataLength = data.length)
147 _resourceLoader->didReceiveData(reinterpret_cast<const char*>(data.bytes), dataLength, lengthReceived, DataPayloadBytes);
150 - (void)connectionDidFinishLoading:(NSURLConnection *)connection
152 ASSERT_UNUSED(connection, !connection);
153 if (_resourceLoader->reachedTerminalState())
156 ASSERT(_hasSentDidReceiveResponse);
158 NetworkLoadMetrics emptyMetrics;
159 _resourceLoader->didFinishLoading(emptyMetrics);
162 static inline bool isQuickLookPasswordError(NSError *error)
164 return error.code == kQLReturnPasswordProtected && [error.domain isEqualToString:@"QuickLookErrorDomain"];
167 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
169 ASSERT_UNUSED(connection, !connection);
170 if (_resourceLoader->reachedTerminalState())
173 if (!isQuickLookPasswordError(error)) {
174 [self _sendDidReceiveResponseIfNecessary];
175 _resourceLoader->didFail(error);
179 if (!_client->supportsPasswordEntry()) {
180 _resourceLoader->didFail(_resourceLoader->cannotShowURLError());
184 _client->didRequestPassword([self, retainedSelf = retainPtr(self)] (const String& password) {
185 _converter = std::make_unique<PreviewConverter>(self, _response, password);
186 [_converter->platformConverter() appendDataArray:_bufferedDataArray.get()];
187 [_converter->platformConverter() finishedAppendingData];
195 PreviewLoader::PreviewLoader(ResourceLoader& loader, const ResourceResponse& response)
196 : m_previewLoader { adoptNS([[WebPreviewLoader alloc] initWithResourceLoader:loader resourceResponse:response]) }
200 PreviewLoader::~PreviewLoader()
204 bool PreviewLoader::shouldCreateForMIMEType(const String& mimeType)
206 return [QLPreviewGetSupportedMIMETypesSet() containsObject:mimeType];
209 std::unique_ptr<PreviewLoader> PreviewLoader::create(ResourceLoader& loader, const ResourceResponse& response)
211 ASSERT(shouldCreateForMIMEType(response.mimeType()));
212 return std::make_unique<PreviewLoader>(loader, response);
215 bool PreviewLoader::didReceiveData(const char* data, unsigned length)
217 if (m_finishedLoadingDataIntoConverter)
220 [m_previewLoader appendDataArray:@[ [NSData dataWithBytes:data length:length] ]];
224 bool PreviewLoader::didReceiveBuffer(const SharedBuffer& buffer)
226 if (m_finishedLoadingDataIntoConverter)
229 [m_previewLoader appendDataArray:buffer.createNSDataArray().get()];
233 bool PreviewLoader::didFinishLoading()
235 if (m_finishedLoadingDataIntoConverter)
238 m_finishedLoadingDataIntoConverter = true;
239 [m_previewLoader finishedAppending];
243 void PreviewLoader::didFail()
245 if (m_finishedLoadingDataIntoConverter)
248 m_finishedLoadingDataIntoConverter = true;
249 [m_previewLoader failed];
250 m_previewLoader = nullptr;
253 void PreviewLoader::setClientForTesting(RefPtr<PreviewLoaderClient>&& client)
255 testingClient() = WTFMove(client);
258 } // namespace WebCore
260 #endif // USE(QUICK_LOOK)