<rdar://problem/13194263> Crashes in NetworkProcess due to threading issues
[WebKit-https.git] / Source / WebKit2 / NetworkProcess / NetworkResourceLoader.cpp
1 /*
2  * Copyright (C) 2012 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 #include "config.h"
27 #include "NetworkResourceLoader.h"
28
29 #if ENABLE(NETWORK_PROCESS)
30
31 #include "AuthenticationManager.h"
32 #include "DataReference.h"
33 #include "Logging.h"
34 #include "NetworkConnectionToWebProcess.h"
35 #include "NetworkProcess.h"
36 #include "NetworkProcessConnectionMessages.h"
37 #include "NetworkResourceLoadParameters.h"
38 #include "PlatformCertificateInfo.h"
39 #include "RemoteNetworkingContext.h"
40 #include "ShareableResource.h"
41 #include "SharedMemory.h"
42 #include "WebCoreArgumentCoders.h"
43 #include "WebResourceLoaderMessages.h"
44 #include <WebCore/NotImplemented.h>
45 #include <WebCore/ResourceBuffer.h>
46 #include <WebCore/ResourceHandle.h>
47 #include <wtf/CurrentTime.h>
48 #include <wtf/MainThread.h>
49
50 using namespace WebCore;
51
52 namespace WebKit {
53
54 NetworkResourceLoader::NetworkResourceLoader(const NetworkResourceLoadParameters& loadParameters, NetworkConnectionToWebProcess* connection)
55     : SchedulableLoader(loadParameters, connection)
56     , m_bytesReceived(0)
57 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090
58     , m_diskCacheTimer(RunLoop::main(), this, &NetworkResourceLoader::diskCacheTimerFired)
59 #endif // __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090
60 {
61     ASSERT(isMainThread());
62 }
63
64 NetworkResourceLoader::~NetworkResourceLoader()
65 {
66     ASSERT(isMainThread());
67     ASSERT(!m_handle);
68 }
69
70 CoreIPC::Connection* NetworkResourceLoader::connection() const
71 {
72     return connectionToWebProcess()->connection();
73 }
74
75 uint64_t NetworkResourceLoader::destinationID() const
76 {
77     return identifier();
78 }
79
80 void NetworkResourceLoader::start()
81 {
82     ASSERT(isMainThread());
83
84     // Explicit ref() balanced by a deref() in NetworkResourceLoader::resourceHandleStopped()
85     ref();
86     
87     // FIXME (NetworkProcess): Create RemoteNetworkingContext with actual settings.
88     m_networkingContext = RemoteNetworkingContext::create(false, false, inPrivateBrowsingMode(), shouldClearReferrerOnHTTPSToHTTPRedirect());
89
90     consumeSandboxExtensions();
91
92     // FIXME (NetworkProcess): Pass an actual value for defersLoading
93     m_handle = ResourceHandle::create(m_networkingContext.get(), request(), this, false /* defersLoading */, contentSniffingPolicy() == SniffContent);
94 }
95
96 static bool performCleanupsCalled = false;
97
98 static Mutex& requestsToCleanupMutex()
99 {
100     DEFINE_STATIC_LOCAL(Mutex, mutex, ());
101     return mutex;
102 }
103
104 static HashSet<NetworkResourceLoader*>& requestsToCleanup()
105 {
106     DEFINE_STATIC_LOCAL(HashSet<NetworkResourceLoader*>, requests, ());
107     return requests;
108 }
109
110 void NetworkResourceLoader::scheduleCleanupOnMainThread()
111 {
112     MutexLocker locker(requestsToCleanupMutex());
113
114     requestsToCleanup().add(this);
115     if (!performCleanupsCalled) {
116         performCleanupsCalled = true;
117         callOnMainThread(NetworkResourceLoader::performCleanups, 0);
118     }
119 }
120
121 void NetworkResourceLoader::performCleanups(void*)
122 {
123     ASSERT(performCleanupsCalled);
124
125     Vector<NetworkResourceLoader*> requests;
126     {
127         MutexLocker locker(requestsToCleanupMutex());
128         copyToVector(requestsToCleanup(), requests);
129         requestsToCleanup().clear();
130         performCleanupsCalled = false;
131     }
132     
133     for (size_t i = 0; i < requests.size(); ++i)
134         requests[i]->cleanup();
135 }
136
137 void NetworkResourceLoader::cleanup()
138 {
139     ASSERT(isMainThread());
140
141     if (FormData* formData = request().httpBody())
142         formData->removeGeneratedFilesIfNeeded();
143
144     // Tell the scheduler about this finished loader soon so it can start more network requests.
145     NetworkProcess::shared().networkResourceLoadScheduler().scheduleRemoveLoader(this);
146
147     if (m_handle) {
148         // Explicit deref() balanced by a ref() in NetworkResourceLoader::start()
149         // This might cause the NetworkResourceLoader to be destroyed and therefore we do it last.
150         m_handle = 0;
151         deref();
152     }
153 }
154
155 void NetworkResourceLoader::connectionToWebProcessDidClose()
156 {
157     ASSERT(isMainThread());
158
159     // If this loader already has a resource handle then it is already in progress on a background thread.
160     // On that thread it will notice that its connection to its WebProcess has been invalidated and it will "gracefully" abort.
161     if (m_handle)
162         return;
163
164 #if !ASSERT_DISABLED
165     // Since there's no handle, this loader should never have been started, and therefore it should never be in the
166     // set of loaders to cleanup on the main thread.
167     // Let's make sure that's true.
168     {
169         MutexLocker locker(requestsToCleanupMutex());
170         ASSERT(!requestsToCleanup().contains(this));
171     }
172 #endif
173
174     cleanup();
175 }
176
177 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090
178 void NetworkResourceLoader::diskCacheTimerFired()
179 {
180     ASSERT(isMainThread());
181     RefPtr<NetworkResourceLoader> adoptedRef = adoptRef(this); // Balance out the ref() when setting the timer.
182
183     ShareableResource::Handle handle;
184     tryGetShareableHandleForResource(handle);
185     if (handle.isNull())
186         return;
187
188     send(Messages::NetworkProcessConnection::DidCacheResource(request(), handle));
189 }
190 #endif // #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090
191
192 template<typename U> bool NetworkResourceLoader::sendAbortingOnFailure(const U& message)
193 {
194     bool result = send(message);
195     if (!result)
196         abortInProgressLoad();
197     return result;
198 }
199
200 template<typename U> bool NetworkResourceLoader::sendSyncAbortingOnFailure(const U& message, const typename U::Reply& reply)
201 {
202     bool result = sendSync(message, reply);
203     if (!result)
204         abortInProgressLoad();
205     return result;
206 }
207
208 void NetworkResourceLoader::abortInProgressLoad()
209 {
210     ASSERT(m_handle);
211     ASSERT(!isMainThread());
212  
213     m_handle->cancel();
214
215     scheduleCleanupOnMainThread();
216 }
217
218 void NetworkResourceLoader::didReceiveResponse(ResourceHandle*, const ResourceResponse& response)
219 {
220     // FIXME (NetworkProcess): Cache the response.
221     if (FormData* formData = request().httpBody())
222         formData->removeGeneratedFilesIfNeeded();
223
224     if (!sendAbortingOnFailure(Messages::WebResourceLoader::DidReceiveResponseWithCertificateInfo(response, PlatformCertificateInfo(response))))
225         return;
226
227 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090
228     ShareableResource::Handle handle;
229     tryGetShareableHandleForResource(handle);
230     if (handle.isNull())
231         return;
232
233     // Since we're delivering this resource by ourselves all at once, we'll abort the resource handle since we don't need anymore callbacks from ResourceHandle.
234     abortInProgressLoad();
235     
236     send(Messages::WebResourceLoader::DidReceiveResource(handle, currentTime()));
237 #endif // __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090
238 }
239
240 void NetworkResourceLoader::didReceiveData(ResourceHandle*, const char* data, int length, int encodedDataLength)
241 {
242     // FIXME (NetworkProcess): For the memory cache we'll also need to cache the response data here.
243     // Such buffering will need to be thread safe, as this callback is happening on a background thread.
244     
245     m_bytesReceived += length;
246
247     CoreIPC::DataReference dataReference(reinterpret_cast<const uint8_t*>(data), length);
248     sendAbortingOnFailure(Messages::WebResourceLoader::DidReceiveData(dataReference, encodedDataLength));
249 }
250
251 void NetworkResourceLoader::didFinishLoading(ResourceHandle*, double finishTime)
252 {
253     // FIXME (NetworkProcess): For the memory cache we'll need to update the finished status of the cached resource here.
254     // Such bookkeeping will need to be thread safe, as this callback is happening on a background thread.
255     invalidateSandboxExtensions();
256     send(Messages::WebResourceLoader::DidFinishResourceLoad(finishTime));
257
258 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090
259     // If this resource was large enough that it should be cached to disk as a separate file,
260     // then we should try to re-deliver the resource to the WebProcess once it *is* saved as a separate file.    
261     if (m_bytesReceived >= fileBackedResourceMinimumSize()) {
262         // FIXME: Once a notification API exists that obliviates this timer, use that instead.
263         ref(); // Balanced by an adoptRef() in diskCacheTimerFired().
264         m_diskCacheTimer.startOneShot(10);
265     }
266 #endif // __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090
267     
268     scheduleCleanupOnMainThread();
269 }
270
271 void NetworkResourceLoader::didFail(ResourceHandle*, const ResourceError& error)
272 {
273     // FIXME (NetworkProcess): For the memory cache we'll need to update the finished status of the cached resource here.
274     // Such bookkeeping will need to be thread safe, as this callback is happening on a background thread.
275     invalidateSandboxExtensions();
276     send(Messages::WebResourceLoader::DidFailResourceLoad(error));
277     scheduleCleanupOnMainThread();
278 }
279
280 void NetworkResourceLoader::willSendRequestAsync(ResourceHandle*, const ResourceRequest& request, const ResourceResponse& redirectResponse)
281 {
282     // We only expect to get the willSendRequest callback from ResourceHandle as the result of a redirect.
283     ASSERT(!redirectResponse.isNull());
284     ASSERT(isMainThread());
285
286     m_suggestedRequestForWillSendRequest = request;
287
288     // This message is DispatchMessageEvenWhenWaitingForSyncReply to avoid a situation where the NetworkProcess is deadlocked waiting for 6 connections
289     // to complete while the WebProcess is waiting for a 7th to complete.
290     connection()->send(Messages::WebResourceLoader::WillSendRequest(request, redirectResponse), destinationID(), CoreIPC::DispatchMessageEvenWhenWaitingForSyncReply);
291 }
292
293 void NetworkResourceLoader::continueWillSendRequest(const ResourceRequest& newRequest)
294 {
295     m_suggestedRequestForWillSendRequest.updateFromDelegatePreservingOldHTTPBody(newRequest.nsURLRequest(DoNotUpdateHTTPBody));
296
297     RunLoop::main()->dispatch(bind(&NetworkResourceLoadScheduler::receivedRedirect, &NetworkProcess::shared().networkResourceLoadScheduler(), this, m_suggestedRequestForWillSendRequest.url()));
298     m_handle->continueWillSendRequest(m_suggestedRequestForWillSendRequest);
299
300     m_suggestedRequestForWillSendRequest = ResourceRequest();
301 }
302
303 // FIXME (NetworkProcess): Many of the following ResourceHandleClient methods definitely need implementations. A few will not.
304 // Once we know what they are they can be removed.
305
306 void NetworkResourceLoader::didSendData(ResourceHandle*, unsigned long long /*bytesSent*/, unsigned long long /*totalBytesToBeSent*/)
307 {
308     notImplemented();
309 }
310
311 void NetworkResourceLoader::didReceiveCachedMetadata(ResourceHandle*, const char*, int)
312 {
313     notImplemented();
314 }
315
316 void NetworkResourceLoader::wasBlocked(ResourceHandle*)
317 {
318     notImplemented();
319 }
320
321 void NetworkResourceLoader::cannotShowURL(ResourceHandle*)
322 {
323     notImplemented();
324 }
325
326 void NetworkResourceLoader::shouldUseCredentialStorageAsync(ResourceHandle* handle)
327 {
328     // When the WebProcess is handling loading a client is consulted each time this shouldUseCredentialStorage question is asked.
329     // In NetworkProcess mode we ask the WebProcess client up front once and then reuse the cached answer.
330
331     handle->continueShouldUseCredentialStorage(allowStoredCredentials() == AllowStoredCredentials);
332 }
333
334 void NetworkResourceLoader::didReceiveAuthenticationChallenge(ResourceHandle*, const AuthenticationChallenge& challenge)
335 {
336     NetworkProcess::shared().authenticationManager().didReceiveAuthenticationChallenge(webPageID(), webFrameID(), challenge);
337 }
338
339 void NetworkResourceLoader::didCancelAuthenticationChallenge(ResourceHandle*, const AuthenticationChallenge& challenge)
340 {
341     // FIXME (NetworkProcess): Tell AuthenticationManager.
342 }
343
344 #if USE(PROTECTION_SPACE_AUTH_CALLBACK)
345 void NetworkResourceLoader::canAuthenticateAgainstProtectionSpaceAsync(ResourceHandle*, const ProtectionSpace& protectionSpace)
346 {
347     ASSERT(isMainThread());
348
349     // This message is DispatchMessageEvenWhenWaitingForSyncReply to avoid a situation where the NetworkProcess is deadlocked
350     // waiting for 6 connections to complete while the WebProcess is waiting for a 7th to complete.
351     connection()->send(Messages::WebResourceLoader::CanAuthenticateAgainstProtectionSpace(protectionSpace), destinationID(), CoreIPC::DispatchMessageEvenWhenWaitingForSyncReply);
352 }
353
354 void NetworkResourceLoader::continueCanAuthenticateAgainstProtectionSpace(bool result)
355 {
356     m_handle->continueCanAuthenticateAgainstProtectionSpace(result);
357 }
358
359 #endif
360
361 #if USE(NETWORK_CFDATA_ARRAY_CALLBACK)
362 bool NetworkResourceLoader::supportsDataArray()
363 {
364     notImplemented();
365     return false;
366 }
367
368 void NetworkResourceLoader::didReceiveDataArray(ResourceHandle*, CFArrayRef)
369 {
370     ASSERT_NOT_REACHED();
371     notImplemented();
372 }
373 #endif
374
375 #if PLATFORM(MAC)
376 void NetworkResourceLoader::willCacheResponseAsync(ResourceHandle* handle, NSCachedURLResponse* response)
377 {
378     notImplemented();
379     handle->continueWillCacheResponse(response);
380 }
381
382 void NetworkResourceLoader::willStopBufferingData(ResourceHandle*, const char*, int)
383 {
384     notImplemented();
385 }
386 #endif // PLATFORM(MAC)
387
388 } // namespace WebKit
389
390 #endif // ENABLE(NETWORK_PROCESS)