96c2c70b2620c227a82ebb6e7ab1c0e91670f4c1
[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::willSendRequest(ResourceHandle*, 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     // IMPORTANT: The fact that this message to the WebProcess is sync is what makes our current approach to synchronous XMLHttpRequests safe.
287     // If this message changes to be asynchronous we might introduce a situation where the NetworkProcess is deadlocked waiting for 6 connections
288     // to complete while the WebProcess is waiting for a 7th to complete.
289     // If we ever change this message to be asynchronous we have to include safeguards to make sure the new design interacts well with sync XHR.
290     ResourceRequest returnedRequest;
291     if (!sendSyncAbortingOnFailure(Messages::WebResourceLoader::WillSendRequest(request, redirectResponse), Messages::WebResourceLoader::WillSendRequest::Reply(returnedRequest)))
292         return;
293     
294     request.updateFromDelegatePreservingOldHTTPBody(returnedRequest.nsURLRequest(DoNotUpdateHTTPBody));
295
296     RunLoop::main()->dispatch(bind(&NetworkResourceLoadScheduler::receivedRedirect, &NetworkProcess::shared().networkResourceLoadScheduler(), this, request.url()));
297 }
298
299 // FIXME (NetworkProcess): Many of the following ResourceHandleClient methods definitely need implementations. A few will not.
300 // Once we know what they are they can be removed.
301
302 void NetworkResourceLoader::didSendData(WebCore::ResourceHandle*, unsigned long long /*bytesSent*/, unsigned long long /*totalBytesToBeSent*/)
303 {
304     notImplemented();
305 }
306
307 void NetworkResourceLoader::didReceiveCachedMetadata(WebCore::ResourceHandle*, const char*, int)
308 {
309     notImplemented();
310 }
311
312 void NetworkResourceLoader::wasBlocked(WebCore::ResourceHandle*)
313 {
314     notImplemented();
315 }
316
317 void NetworkResourceLoader::cannotShowURL(WebCore::ResourceHandle*)
318 {
319     notImplemented();
320 }
321
322 bool NetworkResourceLoader::shouldUseCredentialStorage(ResourceHandle*)
323 {
324     // When the WebProcess is handling loading a client is consulted each time this shouldUseCredentialStorage question is asked.
325     // In NetworkProcess mode we ask the WebProcess client up front once and then reuse the cached answer.
326
327     return allowStoredCredentials() == AllowStoredCredentials;
328 }
329
330 void NetworkResourceLoader::didReceiveAuthenticationChallenge(ResourceHandle*, const AuthenticationChallenge& challenge)
331 {
332     NetworkProcess::shared().authenticationManager().didReceiveAuthenticationChallenge(webPageID(), webFrameID(), challenge);
333 }
334
335 void NetworkResourceLoader::didCancelAuthenticationChallenge(ResourceHandle*, const AuthenticationChallenge& challenge)
336 {
337     // FIXME (NetworkProcess): Tell AuthenticationManager.
338 }
339
340 #if USE(PROTECTION_SPACE_AUTH_CALLBACK)
341 bool NetworkResourceLoader::canAuthenticateAgainstProtectionSpace(ResourceHandle*, const ProtectionSpace& protectionSpace)
342 {
343     ASSERT(!isMainThread());
344
345     // IMPORTANT: The fact that this message to the WebProcess is sync is what makes our current approach to synchronous XMLHttpRequests safe.
346     // If this message changes to be asynchronous we might introduce a situation where the NetworkProcess is deadlocked waiting for 6 connections
347     // to complete while the WebProcess is waiting for a 7th to complete.
348     // If we ever change this message to be asynchronous we have to include safeguards to make sure the new design interacts well with sync XHR.
349     bool result;
350     if (!sendSyncAbortingOnFailure(Messages::WebResourceLoader::CanAuthenticateAgainstProtectionSpace(protectionSpace), Messages::WebResourceLoader::CanAuthenticateAgainstProtectionSpace::Reply(result)))
351         return false;
352
353     return result;
354 }
355 #endif
356
357 #if USE(NETWORK_CFDATA_ARRAY_CALLBACK)
358 bool NetworkResourceLoader::supportsDataArray()
359 {
360     notImplemented();
361     return false;
362 }
363
364 void NetworkResourceLoader::didReceiveDataArray(WebCore::ResourceHandle*, CFArrayRef)
365 {
366     ASSERT_NOT_REACHED();
367     notImplemented();
368 }
369 #endif
370
371 #if PLATFORM(MAC)
372 #if USE(CFNETWORK)
373 CFCachedURLResponseRef NetworkResourceLoader::willCacheResponse(WebCore::ResourceHandle*, CFCachedURLResponseRef response)
374 {
375     notImplemented();
376     return response;
377 }
378 #else
379 NSCachedURLResponse* NetworkResourceLoader::willCacheResponse(WebCore::ResourceHandle*, NSCachedURLResponse* response)
380 {
381     notImplemented();
382     return response;
383 }
384 #endif
385
386 void NetworkResourceLoader::willStopBufferingData(WebCore::ResourceHandle*, const char*, int)
387 {
388     notImplemented();
389 }
390 #endif // PLATFORM(MAC)
391
392 } // namespace WebKit
393
394 #endif // ENABLE(NETWORK_PROCESS)