Shrink various loading-related enums to shrink CachedResource
[WebKit-https.git] / Source / WebCore / platform / network / mac / WebCoreResourceHandleAsOperationQueueDelegate.mm
1 /*
2  * Copyright (C) 2004-2018 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 "WebCoreResourceHandleAsOperationQueueDelegate.h"
28
29 #import "AuthenticationChallenge.h"
30 #import "AuthenticationMac.h"
31 #import "Logging.h"
32 #import "ResourceHandle.h"
33 #import "ResourceHandleClient.h"
34 #import "ResourceRequest.h"
35 #import "ResourceResponse.h"
36 #import "SharedBuffer.h"
37 #import "SynchronousLoaderClient.h"
38 #import "WebCoreURLResponse.h"
39 #import <pal/spi/cf/CFNetworkSPI.h>
40 #import <wtf/BlockPtr.h>
41 #import <wtf/MainThread.h>
42
43 using namespace WebCore;
44
45 static bool scheduledWithCustomRunLoopMode(const std::optional<SchedulePairHashSet>& pairs)
46 {
47     if (!pairs)
48         return false;
49     for (auto& pair : *pairs) {
50         auto mode = pair->mode();
51         if (mode != kCFRunLoopCommonModes && mode != kCFRunLoopDefaultMode)
52             return true;
53     }
54     return false;
55 }
56
57 @implementation WebCoreResourceHandleAsOperationQueueDelegate
58
59 - (void)callFunctionOnMainThread:(Function<void()>&&)function
60 {
61     // Sync xhr uses the message queue.
62     if (m_messageQueue)
63         return m_messageQueue->append(std::make_unique<Function<void()>>(WTFMove(function)));
64
65     // This is the common case.
66     if (!scheduledWithCustomRunLoopMode(m_scheduledPairs))
67         return callOnMainThread(WTFMove(function));
68
69     // If we have been scheduled in a custom run loop mode, schedule a block in that mode.
70     auto block = BlockPtr<void()>::fromCallable([alreadyCalled = false, function = WTFMove(function)] () mutable {
71         if (alreadyCalled)
72             return;
73         alreadyCalled = true;
74         function();
75         function = nullptr;
76     });
77     for (auto& pair : *m_scheduledPairs)
78         CFRunLoopPerformBlock(pair->runLoop(), pair->mode(), block.get());
79 }
80
81 - (id)initWithHandle:(ResourceHandle*)handle messageQueue:(MessageQueue<Function<void()>>*)messageQueue
82 {
83     self = [self init];
84     if (!self)
85         return nil;
86
87     m_handle = handle;
88     if (m_handle && m_handle->context()) {
89         if (auto* pairs = m_handle->context()->scheduledRunLoopPairs())
90             m_scheduledPairs = *pairs;
91     }
92     m_semaphore = dispatch_semaphore_create(0);
93     m_messageQueue = messageQueue;
94
95     return self;
96 }
97
98 - (void)detachHandle
99 {
100     LockHolder lock(m_mutex);
101
102     m_handle = nullptr;
103
104     m_messageQueue = nullptr;
105     m_requestResult = nullptr;
106     m_cachedResponseResult = nullptr;
107     m_boolResult = NO;
108     dispatch_semaphore_signal(m_semaphore); // OK to signal even if we are not waiting.
109 }
110
111 - (void)dealloc
112 {
113     dispatch_release(m_semaphore);
114     [super dealloc];
115 }
116
117 - (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)newRequest redirectResponse:(NSURLResponse *)redirectResponse
118 {
119     ASSERT(!isMainThread());
120     UNUSED_PARAM(connection);
121
122     redirectResponse = synthesizeRedirectResponseIfNecessary([connection currentRequest], newRequest, redirectResponse);
123
124     // See <rdar://problem/5380697>. This is a workaround for a behavior change in CFNetwork where willSendRequest gets called more often.
125     if (!redirectResponse)
126         return newRequest;
127
128 #if !LOG_DISABLED
129     if ([redirectResponse isKindOfClass:[NSHTTPURLResponse class]])
130         LOG(Network, "Handle %p delegate connection:%p willSendRequest:%@ redirectResponse:%d, Location:<%@>", m_handle, connection, [newRequest description], static_cast<int>([(id)redirectResponse statusCode]), [[(id)redirectResponse allHeaderFields] objectForKey:@"Location"]);
131     else
132         LOG(Network, "Handle %p delegate connection:%p willSendRequest:%@ redirectResponse:non-HTTP", m_handle, connection, [newRequest description]); 
133 #endif
134
135     auto protectedSelf = retainPtr(self);
136     auto work = [self, protectedSelf, newRequest = retainPtr(newRequest), redirectResponse = retainPtr(redirectResponse)] () mutable {
137         if (!m_handle) {
138             m_requestResult = nullptr;
139             dispatch_semaphore_signal(m_semaphore);
140             return;
141         }
142
143         m_handle->willSendRequest(newRequest.get(), redirectResponse.get(), [self, protectedSelf = WTFMove(protectedSelf)](ResourceRequest&& request) {
144             m_requestResult = request.nsURLRequest(HTTPBodyUpdatePolicy::UpdateHTTPBody);
145             dispatch_semaphore_signal(m_semaphore);
146         });
147     };
148
149     [self callFunctionOnMainThread:WTFMove(work)];
150     dispatch_semaphore_wait(m_semaphore, DISPATCH_TIME_FOREVER);
151
152     LockHolder lock(m_mutex);
153     if (!m_handle)
154         return nil;
155
156     RetainPtr<NSURLRequest> requestResult = m_requestResult;
157
158     // Make sure protectedSelf gets destroyed on the main thread in case this is the last strong reference to self
159     // as we do not want to get destroyed on a non-main thread.
160     [self callFunctionOnMainThread:[protectedSelf = WTFMove(protectedSelf)] { }];
161
162     return requestResult.autorelease();
163 }
164
165 - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
166 {
167     ASSERT(!isMainThread());
168     UNUSED_PARAM(connection);
169
170     LOG(Network, "Handle %p delegate connection:%p didReceiveAuthenticationChallenge:%p", m_handle, connection, challenge);
171
172     auto work = [self, protectedSelf = retainPtr(self), challenge = retainPtr(challenge)] () mutable {
173         if (!m_handle) {
174             [[challenge sender] cancelAuthenticationChallenge:challenge.get()];
175             return;
176         }
177         m_handle->didReceiveAuthenticationChallenge(core(challenge.get()));
178     };
179
180     [self callFunctionOnMainThread:WTFMove(work)];
181 }
182
183 - (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
184 {
185     ASSERT(!isMainThread());
186     UNUSED_PARAM(connection);
187
188     LOG(Network, "Handle %p delegate connection:%p canAuthenticateAgainstProtectionSpace:%@://%@:%u realm:%@ method:%@ %@%@", m_handle, connection, [protectionSpace protocol], [protectionSpace host], [protectionSpace port], [protectionSpace realm], [protectionSpace authenticationMethod], [protectionSpace isProxy] ? @"proxy:" : @"", [protectionSpace isProxy] ? [protectionSpace proxyType] : @"");
189
190     auto protectedSelf = retainPtr(self);
191     auto work = [self, protectedSelf, protectionSpace = retainPtr(protectionSpace)] () mutable {
192         if (!m_handle) {
193             m_boolResult = NO;
194             dispatch_semaphore_signal(m_semaphore);
195             return;
196         }
197         m_handle->canAuthenticateAgainstProtectionSpace(ProtectionSpace(protectionSpace.get()), [self, protectedSelf = WTFMove(protectedSelf)] (bool result) mutable {
198             m_boolResult = result;
199             dispatch_semaphore_signal(m_semaphore);
200         });
201     };
202
203     [self callFunctionOnMainThread:WTFMove(work)];
204     dispatch_semaphore_wait(m_semaphore, DISPATCH_TIME_FOREVER);
205
206     LockHolder lock(m_mutex);
207     if (!m_handle)
208         return NO;
209
210     auto boolResult = m_boolResult;
211
212     // Make sure protectedSelf gets destroyed on the main thread in case this is the last strong reference to self
213     // as we do not want to get destroyed on a non-main thread.
214     [self callFunctionOnMainThread:[protectedSelf = WTFMove(protectedSelf)] { }];
215
216     return boolResult;
217 }
218
219 - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)r
220 {
221     ASSERT(!isMainThread());
222
223     LOG(Network, "Handle %p delegate connection:%p didReceiveResponse:%p (HTTP status %d, reported MIMEType '%s')", m_handle, connection, r, [r respondsToSelector:@selector(statusCode)] ? [(id)r statusCode] : 0, [[r MIMEType] UTF8String]);
224
225     auto protectedSelf = retainPtr(self);
226     auto work = [self, protectedSelf, r = retainPtr(r), connection = retainPtr(connection)] () mutable {
227         RefPtr<ResourceHandle> protectedHandle(m_handle);
228         if (!m_handle || !m_handle->client()) {
229             dispatch_semaphore_signal(m_semaphore);
230             return;
231         }
232
233         // Avoid MIME type sniffing if the response comes back as 304 Not Modified.
234         int statusCode = [r respondsToSelector:@selector(statusCode)] ? [(id)r statusCode] : 0;
235         if (statusCode != 304) {
236             bool isMainResourceLoad = m_handle->firstRequest().requester() == ResourceRequest::Requester::Main;
237             adjustMIMETypeIfNecessary([r _CFURLResponse], isMainResourceLoad);
238         }
239
240         if ([m_handle->firstRequest().nsURLRequest(HTTPBodyUpdatePolicy::DoNotUpdateHTTPBody) _propertyForKey:@"ForceHTMLMIMEType"])
241             [r _setMIMEType:@"text/html"];
242
243         ResourceResponse resourceResponse(r.get());
244         resourceResponse.setSource(ResourceResponse::Source::Network);
245         ResourceHandle::getConnectionTimingData(connection.get(), resourceResponse.deprecatedNetworkLoadMetrics());
246
247         m_handle->didReceiveResponse(WTFMove(resourceResponse), [self, protectedSelf = WTFMove(protectedSelf)] {
248             dispatch_semaphore_signal(m_semaphore);
249         });
250     };
251
252     [self callFunctionOnMainThread:WTFMove(work)];
253     dispatch_semaphore_wait(m_semaphore, DISPATCH_TIME_FOREVER);
254
255     // Make sure we get destroyed on the main thread.
256     [self callFunctionOnMainThread:[protectedSelf = WTFMove(protectedSelf)] { }];
257 }
258
259 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data lengthReceived:(long long)lengthReceived
260 {
261     ASSERT(!isMainThread());
262     UNUSED_PARAM(connection);
263     UNUSED_PARAM(lengthReceived);
264
265     LOG(Network, "Handle %p delegate connection:%p didReceiveData:%p lengthReceived:%lld", m_handle, connection, data, lengthReceived);
266
267     auto work = [self = self, protectedSelf = retainPtr(self), data = retainPtr(data)] () mutable {
268         if (!m_handle || !m_handle->client())
269             return;
270         // FIXME: If we get more than 2B bytes in a single chunk, this code won't do the right thing.
271         // However, with today's computers and networking speeds, this won't happen in practice.
272         // Could be an issue with a giant local file.
273
274         // FIXME: https://bugs.webkit.org/show_bug.cgi?id=19793
275         // -1 means we do not provide any data about transfer size to inspector so it would use
276         // Content-Length headers or content size to show transfer size.
277         m_handle->client()->didReceiveBuffer(m_handle, SharedBuffer::create(data.get()), -1);
278     };
279
280     [self callFunctionOnMainThread:WTFMove(work)];
281 }
282
283 - (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite
284 {
285     ASSERT(!isMainThread());
286     UNUSED_PARAM(connection);
287     UNUSED_PARAM(bytesWritten);
288
289     LOG(Network, "Handle %p delegate connection:%p didSendBodyData:%d totalBytesWritten:%d totalBytesExpectedToWrite:%d", m_handle, connection, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
290
291     auto work = [self = self, protectedSelf = retainPtr(self), totalBytesWritten = totalBytesWritten, totalBytesExpectedToWrite = totalBytesExpectedToWrite] () mutable {
292         if (!m_handle || !m_handle->client())
293             return;
294         m_handle->client()->didSendData(m_handle, totalBytesWritten, totalBytesExpectedToWrite);
295     };
296
297     [self callFunctionOnMainThread:WTFMove(work)];
298 }
299
300 - (void)connectionDidFinishLoading:(NSURLConnection *)connection
301 {
302     ASSERT(!isMainThread());
303     UNUSED_PARAM(connection);
304
305     LOG(Network, "Handle %p delegate connectionDidFinishLoading:%p", m_handle, connection);
306
307     auto work = [self = self, protectedSelf = retainPtr(self)] () mutable {
308         if (!m_handle || !m_handle->client())
309             return;
310
311         m_handle->client()->didFinishLoading(m_handle);
312         if (m_messageQueue) {
313             m_messageQueue->kill();
314             m_messageQueue = nullptr;
315         }
316     };
317
318     [self callFunctionOnMainThread:WTFMove(work)];
319 }
320
321 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
322 {
323     ASSERT(!isMainThread());
324     UNUSED_PARAM(connection);
325
326     LOG(Network, "Handle %p delegate connection:%p didFailWithError:%@", m_handle, connection, error);
327
328     auto work = [self = self, protectedSelf = retainPtr(self), error = retainPtr(error)] () mutable {
329         if (!m_handle || !m_handle->client())
330             return;
331
332         m_handle->client()->didFail(m_handle, error.get());
333         if (m_messageQueue) {
334             m_messageQueue->kill();
335             m_messageQueue = nullptr;
336         }
337     };
338
339     [self callFunctionOnMainThread:WTFMove(work)];
340 }
341
342
343 - (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse
344 {
345     ASSERT(!isMainThread());
346     UNUSED_PARAM(connection);
347
348     LOG(Network, "Handle %p delegate connection:%p willCacheResponse:%p", m_handle, connection, cachedResponse);
349
350     auto protectedSelf = retainPtr(self);
351     auto work = [self, protectedSelf, cachedResponse = retainPtr(cachedResponse)] () mutable {
352         if (!m_handle || !m_handle->client()) {
353             m_cachedResponseResult = nullptr;
354             dispatch_semaphore_signal(m_semaphore);
355             return;
356         }
357
358         m_handle->client()->willCacheResponseAsync(m_handle, cachedResponse.get(), [self, protectedSelf = WTFMove(protectedSelf)] (NSCachedURLResponse * response) mutable {
359             m_cachedResponseResult = response;
360             dispatch_semaphore_signal(m_semaphore);
361         });
362     };
363
364     [self callFunctionOnMainThread:WTFMove(work)];
365     dispatch_semaphore_wait(m_semaphore, DISPATCH_TIME_FOREVER);
366
367     LockHolder lock(m_mutex);
368     if (!m_handle)
369         return nil;
370
371     RetainPtr<NSCachedURLResponse> cachedResponseResult = m_cachedResponseResult;
372
373     // Make sure protectedSelf gets destroyed on the main thread in case this is the last strong reference to self
374     // as we do not want to get destroyed on a non-main thread.
375     [self callFunctionOnMainThread:[protectedSelf = WTFMove(protectedSelf)] { }];
376
377     return cachedResponseResult.autorelease();
378 }
379
380 @end
381
382 @implementation WebCoreResourceHandleWithCredentialStorageAsOperationQueueDelegate
383
384 - (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection
385 {
386     ASSERT(!isMainThread());
387     UNUSED_PARAM(connection);
388     return NO;
389 }
390
391 @end