Move NetworkStorageSession ownership to NetworkProcess
[WebKit-https.git] / Source / WebKitLegacy / win / WebDownloadCFNet.cpp
1 /*
2  * Copyright (C) 2007, 2015 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. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "WebKitDLL.h"
27 #include "WebDownload.h"
28
29 #include "DefaultDownloadDelegate.h"
30 #include "MarshallingHelpers.h"
31 #include "NetworkStorageSessionMap.h"
32 #include "WebError.h"
33 #include "WebKit.h"
34 #include "WebKitLogging.h"
35 #include "WebMutableURLRequest.h"
36 #include "WebURLAuthenticationChallenge.h"
37 #include "WebURLCredential.h"
38 #include "WebURLResponse.h"
39
40 #include <wtf/text/CString.h>
41
42 #include <io.h>
43 #include <sys/stat.h>
44 #include <sys/types.h>
45
46 #include <WebCore/AuthenticationCF.h>
47 #include <WebCore/BString.h>
48 #include <WebCore/CredentialStorage.h>
49 #include <WebCore/DownloadBundle.h>
50 #include <WebCore/LoaderRunLoopCF.h>
51 #include <WebCore/NetworkStorageSession.h>
52 #include <WebCore/ResourceError.h>
53 #include <WebCore/ResourceHandle.h>
54 #include <WebCore/ResourceRequest.h>
55 #include <WebCore/ResourceResponse.h>
56
57 using namespace WebCore;
58
59 // CFURLDownload Callbacks ----------------------------------------------------------------
60 static void didStartCallback(CFURLDownloadRef download, const void *clientInfo);
61 static CFURLRequestRef willSendRequestCallback(CFURLDownloadRef download, CFURLRequestRef request, CFURLResponseRef redirectionResponse, const void *clientInfo);
62 static void didReceiveAuthenticationChallengeCallback(CFURLDownloadRef download, CFURLAuthChallengeRef challenge, const void *clientInfo);
63 static void didReceiveResponseCallback(CFURLDownloadRef download, CFURLResponseRef response, const void *clientInfo);
64 static void willResumeWithResponseCallback(CFURLDownloadRef download, CFURLResponseRef response, UInt64 startingByte, const void *clientInfo);
65 static void didReceiveDataCallback(CFURLDownloadRef download, CFIndex length, const void *clientInfo);
66 static Boolean shouldDecodeDataOfMIMETypeCallback(CFURLDownloadRef download, CFStringRef encodingType, const void *clientInfo);
67 static void decideDestinationWithSuggestedObjectNameCallback(CFURLDownloadRef download, CFStringRef objectName, const void *clientInfo);
68 static void didCreateDestinationCallback(CFURLDownloadRef download, CFURLRef path, const void *clientInfo);
69 static void didFinishCallback(CFURLDownloadRef download, const void *clientInfo);
70 static void didFailCallback(CFURLDownloadRef download, CFErrorRef error, const void *clientInfo);
71
72 void WebDownload::init(ResourceHandle* handle, const ResourceRequest& request, const ResourceResponse& response, IWebDownloadDelegate* delegate)
73 {
74     m_delegate = delegate ? delegate : DefaultDownloadDelegate::sharedInstance();
75     CFURLConnectionRef connection = handle->connection();
76     if (!connection) {
77         LOG_ERROR("WebDownload::WebDownload(ResourceHandle*,...) called with an inactive ResourceHandle");    
78         return;
79     }
80
81     CFURLDownloadClient client = {0, this, 0, 0, 0, didStartCallback, willSendRequestCallback, didReceiveAuthenticationChallengeCallback, 
82         didReceiveResponseCallback, willResumeWithResponseCallback, didReceiveDataCallback, shouldDecodeDataOfMIMETypeCallback,
83         decideDestinationWithSuggestedObjectNameCallback, didCreateDestinationCallback, didFinishCallback, didFailCallback};
84
85     m_request.adoptRef(WebMutableURLRequest::createInstance(request));
86     m_download = adoptCF(CFURLDownloadCreateAndStartWithLoadingConnection(0, connection, request.cfURLRequest(UpdateHTTPBody), response.cfURLResponse(), &client));
87
88     // It is possible for CFURLDownloadCreateAndStartWithLoadingConnection() to fail if the passed in CFURLConnection is not in a "downloadable state"
89     // However, we should never hit that case
90     if (!m_download) {
91         ASSERT_NOT_REACHED();
92         LOG_ERROR("WebDownload - Failed to create WebDownload from existing connection (%s)", request.url().string().utf8().data());
93     } else
94         LOG(Download, "WebDownload - Created WebDownload %p from existing connection (%s)", this, request.url().string().utf8().data());
95
96     // The CFURLDownload either starts successfully and retains the CFURLConnection, 
97     // or it fails to creating and we have a now-useless connection with a dangling ref. 
98     // Either way, we need to release the connection to balance out ref counts
99     handle->releaseConnectionForDownload();
100 }
101
102 void WebDownload::init(const URL& url, IWebDownloadDelegate* delegate)
103 {
104     m_delegate = delegate ? delegate : DefaultDownloadDelegate::sharedInstance();
105     LOG_ERROR("Delegate is %p", m_delegate.get());
106
107     ResourceRequest request(url);
108     CFURLRequestRef cfRequest = request.cfURLRequest(UpdateHTTPBody);
109
110     CFURLDownloadClient client = {0, this, 0, 0, 0, didStartCallback, willSendRequestCallback, didReceiveAuthenticationChallengeCallback, 
111                                   didReceiveResponseCallback, willResumeWithResponseCallback, didReceiveDataCallback, shouldDecodeDataOfMIMETypeCallback, 
112                                   decideDestinationWithSuggestedObjectNameCallback, didCreateDestinationCallback, didFinishCallback, didFailCallback};
113     m_request.adoptRef(WebMutableURLRequest::createInstance(request));
114     m_download = adoptCF(CFURLDownloadCreate(0, cfRequest, &client));
115
116     CFURLDownloadScheduleWithCurrentMessageQueue(m_download.get());
117     CFURLDownloadScheduleDownloadWithRunLoop(m_download.get(), loaderRunLoop(), kCFRunLoopDefaultMode);
118
119     LOG(Download, "WebDownload - Initialized download of url %s in WebDownload %p", url.string().utf8().data(), this);
120 }
121
122 // IWebDownload -------------------------------------------------------------------
123
124 HRESULT WebDownload::initWithRequest(_In_opt_ IWebURLRequest* request, _In_opt_ IWebDownloadDelegate* delegate)
125 {
126     COMPtr<WebMutableURLRequest> webRequest;
127     if (!request || FAILED(request->QueryInterface(&webRequest))) {
128         LOG(Download, "WebDownload - initWithRequest failed - not a WebMutableURLRequest");    
129         return E_FAIL;
130     }
131
132     if (!delegate)
133         return E_FAIL;
134     m_delegate = delegate;
135     LOG(Download, "Delegate is %p", m_delegate.get());
136
137     RetainPtr<CFURLRequestRef> cfRequest = webRequest->resourceRequest().cfURLRequest(UpdateHTTPBody);
138
139     CFURLDownloadClient client = {0, this, 0, 0, 0, didStartCallback, willSendRequestCallback, didReceiveAuthenticationChallengeCallback, 
140                                   didReceiveResponseCallback, willResumeWithResponseCallback, didReceiveDataCallback, shouldDecodeDataOfMIMETypeCallback, 
141                                   decideDestinationWithSuggestedObjectNameCallback, didCreateDestinationCallback, didFinishCallback, didFailCallback};
142     m_request.adoptRef(WebMutableURLRequest::createInstance(webRequest.get()));
143     m_download = adoptCF(CFURLDownloadCreate(0, cfRequest.get(), &client));
144
145     // If for some reason the download failed to create, 
146     // we have particular cleanup to do
147     if (!m_download) {
148         m_request = nullptr;    
149         return E_FAIL;
150     }
151
152     CFURLDownloadScheduleWithCurrentMessageQueue(m_download.get());
153     CFURLDownloadScheduleDownloadWithRunLoop(m_download.get(), loaderRunLoop(), kCFRunLoopDefaultMode);
154
155     LOG(Download, "WebDownload - initWithRequest complete, started download of url %s", webRequest->resourceRequest().url().string().utf8().data());
156     return S_OK;
157 }
158
159 HRESULT WebDownload::initToResumeWithBundle(_In_ BSTR bundlePath, _In_opt_ IWebDownloadDelegate* delegate)
160 {
161     LOG(Download, "Attempting resume of download bundle %s", String(bundlePath, SysStringLen(bundlePath)).ascii().data());
162
163     Vector<char> buffer;
164     if (!DownloadBundle::extractResumeData(String(bundlePath, SysStringLen(bundlePath)), buffer))
165         return E_FAIL;
166
167     // It is possible by some twist of fate the bundle magic number was naturally at the end of the file and its not actually a valid bundle.
168     // That, or someone engineered it that way to try to attack us. In that cause, this CFData will successfully create but when we actually
169     // try to start the CFURLDownload using this bogus data, it will fail and we will handle that gracefully.
170     RetainPtr<CFDataRef> resumeData = adoptCF(CFDataCreate(0, reinterpret_cast<const UInt8*>(buffer.data()), buffer.size()));
171
172     if (!delegate)
173         return E_FAIL;
174     m_delegate = delegate;
175     LOG(Download, "Delegate is %p", m_delegate.get());
176
177     CFURLDownloadClient client = {0, this, 0, 0, 0, didStartCallback, willSendRequestCallback, didReceiveAuthenticationChallengeCallback, 
178                                   didReceiveResponseCallback, willResumeWithResponseCallback, didReceiveDataCallback, shouldDecodeDataOfMIMETypeCallback, 
179                                   decideDestinationWithSuggestedObjectNameCallback, didCreateDestinationCallback, didFinishCallback, didFailCallback};
180     
181     RetainPtr<CFURLRef> pathURL = adoptCF(MarshallingHelpers::PathStringToFileCFURLRef(String(bundlePath, SysStringLen(bundlePath))));
182     ASSERT(pathURL);
183
184     m_download = adoptCF(CFURLDownloadCreateWithResumeData(0, resumeData.get(), pathURL.get(), &client));
185
186     if (!m_download) {
187         LOG(Download, "Failed to create CFURLDownloadRef for resume");    
188         return E_FAIL;
189     }
190     
191     m_bundlePath = String(bundlePath, SysStringLen(bundlePath));
192     // Attempt to remove the ".download" extension from the bundle for the final file destination
193     // Failing that, we clear m_destination and will ask the delegate later once the download starts
194     if (m_bundlePath.endsWithIgnoringASCIICase(DownloadBundle::fileExtension())) {
195         m_destination = m_bundlePath.isolatedCopy();
196         m_destination.truncate(m_destination.length() - DownloadBundle::fileExtension().length());
197     } else
198         m_destination = String();
199     
200     CFURLDownloadScheduleWithCurrentMessageQueue(m_download.get());
201     CFURLDownloadScheduleDownloadWithRunLoop(m_download.get(), loaderRunLoop(), kCFRunLoopDefaultMode);
202
203     LOG(Download, "WebDownload - initWithRequest complete, resumed download of bundle %s", String(bundlePath, SysStringLen(bundlePath)).ascii().data());
204     return S_OK;
205 }
206
207 HRESULT WebDownload::start()
208 {
209     LOG(Download, "WebDownload - Starting download (%p)", this);
210     if (!m_download)
211         return E_FAIL;
212
213     CFURLDownloadStart(m_download.get());
214     // FIXME: 4950477 - CFURLDownload neglects to make the didStart() client call upon starting the download.
215     // This is a somewhat critical call, so we'll fake it for now!
216     didStart();
217
218     return S_OK;
219 }
220
221 HRESULT WebDownload::cancel()
222 {
223     LOG(Download, "WebDownload - Cancelling download (%p)", this);
224     if (!m_download)
225         return E_FAIL;
226
227     CFURLDownloadCancel(m_download.get());
228     m_download = nullptr;
229     return S_OK;
230 }
231
232 HRESULT WebDownload::cancelForResume()
233 {
234     LOG(Download, "WebDownload - Cancelling download (%p), writing resume information to file if possible", this);
235     ASSERT(m_download);
236     if (!m_download)
237         return E_FAIL;
238
239     HRESULT hr = S_OK;
240     RetainPtr<CFDataRef> resumeData;
241     if (m_destination.isEmpty()) {
242         CFURLDownloadCancel(m_download.get());
243         goto exit;
244     }
245
246     CFURLDownloadSetDeletesUponFailure(m_download.get(), false);
247     CFURLDownloadCancel(m_download.get());
248
249     resumeData = adoptCF(CFURLDownloadCopyResumeData(m_download.get()));
250     if (!resumeData) {
251         LOG(Download, "WebDownload - Unable to create resume data for download (%p)", this);
252         goto exit;
253     }
254
255     const char* resumeBytes = reinterpret_cast<const char*>(CFDataGetBytePtr(resumeData.get()));
256     uint32_t resumeLength = CFDataGetLength(resumeData.get());
257     DownloadBundle::appendResumeData(resumeBytes, resumeLength, m_bundlePath);
258
259 exit:
260     m_download = nullptr;
261     return hr;
262 }
263
264 HRESULT WebDownload::deletesFileUponFailure(_Out_ BOOL* result)
265 {
266     if (!result)
267         return E_POINTER;
268     *result = FALSE;
269     if (!m_download)
270         return E_FAIL;
271     *result = CFURLDownloadDeletesUponFailure(m_download.get());
272     return S_OK;
273 }
274
275 HRESULT WebDownload::setDeletesFileUponFailure(BOOL deletesFileUponFailure)
276 {
277     if (!m_download)
278         return E_FAIL;
279     CFURLDownloadSetDeletesUponFailure(m_download.get(), !!deletesFileUponFailure);
280     return S_OK;
281 }
282
283 HRESULT WebDownload::setDestination(_In_ BSTR path, BOOL allowOverwrite)
284 {
285     if (!m_download)
286         return E_FAIL;
287
288     m_destination = String(path, SysStringLen(path));
289     m_bundlePath = m_destination + DownloadBundle::fileExtension();
290
291     CFURLRef pathURL = MarshallingHelpers::PathStringToFileCFURLRef(m_bundlePath);
292     CFURLDownloadSetDestination(m_download.get(), pathURL, !!allowOverwrite);
293     CFRelease(pathURL);
294
295     LOG(Download, "WebDownload - Set destination to %s", m_bundlePath.ascii().data());
296
297     return S_OK;
298 }
299
300 // IWebURLAuthenticationChallengeSender -------------------------------------------------------------------
301
302 HRESULT WebDownload::cancelAuthenticationChallenge(_In_opt_ IWebURLAuthenticationChallenge*)
303 {
304     if (m_download) {
305         CFURLDownloadCancel(m_download.get());
306         m_download = nullptr;
307     }
308
309     // FIXME: Do we need a URL or description for this error code?
310     ResourceError error(String(WebURLErrorDomain), WebURLErrorUserCancelledAuthentication, URL(), "");
311     COMPtr<WebError> webError(AdoptCOM, WebError::createInstance(error));
312     m_delegate->didFailWithError(this, webError.get());
313
314     return S_OK;
315 }
316
317 HRESULT WebDownload::continueWithoutCredentialForAuthenticationChallenge(_In_opt_ IWebURLAuthenticationChallenge* challenge)
318 {
319     COMPtr<WebURLAuthenticationChallenge> webChallenge(Query, challenge);
320     if (!webChallenge)
321         return E_NOINTERFACE;
322
323     if (m_download)
324         CFURLDownloadUseCredential(m_download.get(), 0, webChallenge->authenticationChallenge().cfURLAuthChallengeRef());
325     return S_OK;
326 }
327
328 HRESULT WebDownload::useCredential(_In_opt_ IWebURLCredential* credential, _In_opt_ IWebURLAuthenticationChallenge* challenge)
329 {
330     COMPtr<WebURLAuthenticationChallenge> webChallenge(Query, challenge);
331     if (!webChallenge)
332         return E_NOINTERFACE;
333
334     COMPtr<WebURLCredential> webCredential(Query, credential);
335     if (!webCredential)
336         return E_NOINTERFACE;
337
338     RetainPtr<CFURLCredentialRef> cfCredential = adoptCF(createCF(webCredential->credential()));
339
340     if (m_download)
341         CFURLDownloadUseCredential(m_download.get(), cfCredential.get(), webChallenge->authenticationChallenge().cfURLAuthChallengeRef());
342     return S_OK;
343 }
344
345 // CFURLDownload Callbacks -------------------------------------------------------------------
346 void WebDownload::didStart()
347 {
348 #ifndef NDEBUG
349     m_startTime = m_dataTime = WallTime::now();
350     m_received = 0;
351     LOG(Download, "DOWNLOAD - Started %p at %.3f seconds", this, m_startTime.secondsSinceEpoch().seconds());
352 #endif
353     if (FAILED(m_delegate->didBegin(this)))
354         LOG_ERROR("DownloadDelegate->didBegin failed");
355 }
356
357 CFURLRequestRef WebDownload::willSendRequest(CFURLRequestRef request, CFURLResponseRef response)
358 {
359     COMPtr<WebMutableURLRequest> webRequest(AdoptCOM, WebMutableURLRequest::createInstance(ResourceRequest(request)));
360     COMPtr<WebURLResponse> webResponse(AdoptCOM, WebURLResponse::createInstance(ResourceResponse(response)));
361     COMPtr<IWebMutableURLRequest> finalRequest;
362
363     if (FAILED(m_delegate->willSendRequest(this, webRequest.get(), webResponse.get(), &finalRequest)))
364         LOG_ERROR("DownloadDelegate->willSendRequest failed");
365     
366     if (!finalRequest)
367         return 0;
368
369     COMPtr<WebMutableURLRequest> finalWebRequest(AdoptCOM, WebMutableURLRequest::createInstance(finalRequest.get()));
370     m_request = finalWebRequest.get();
371     CFURLRequestRef result = finalWebRequest->resourceRequest().cfURLRequest(UpdateHTTPBody);
372     CFRetain(result);
373     return result;
374 }
375
376 void WebDownload::didReceiveAuthenticationChallenge(CFURLAuthChallengeRef challenge)
377 {
378     // Try previously stored credential first.
379     if (!CFURLAuthChallengeGetPreviousFailureCount(challenge)) {
380         Credential credential = NetworkStorageSessionMap::defaultStorageSession().credentialStorage().get(emptyString(), core(CFURLAuthChallengeGetProtectionSpace(challenge)));
381         if (!credential.isEmpty()) {
382             RetainPtr<CFURLCredentialRef> cfCredential = adoptCF(createCF(credential));
383             CFURLDownloadUseCredential(m_download.get(), cfCredential.get(), challenge);
384             return;
385         }
386     }
387
388     COMPtr<IWebURLAuthenticationChallenge> webChallenge(AdoptCOM,
389         WebURLAuthenticationChallenge::createInstance(AuthenticationChallenge(challenge, 0), this));
390
391     if (SUCCEEDED(m_delegate->didReceiveAuthenticationChallenge(this, webChallenge.get())))
392         return;
393
394     cancelAuthenticationChallenge(webChallenge.get());
395 }
396
397 void WebDownload::didReceiveResponse(CFURLResponseRef response)
398 {
399     COMPtr<WebURLResponse> webResponse(AdoptCOM, WebURLResponse::createInstance(ResourceResponse(response)));
400     if (FAILED(m_delegate->didReceiveResponse(this, webResponse.get())))
401         LOG_ERROR("DownloadDelegate->didReceiveResponse failed");
402 }
403
404 void WebDownload::willResumeWithResponse(CFURLResponseRef response, UInt64 fromByte)
405 {
406     COMPtr<WebURLResponse> webResponse(AdoptCOM, WebURLResponse::createInstance(ResourceResponse(response)));
407     if (FAILED(m_delegate->willResumeWithResponse(this, webResponse.get(), fromByte)))
408         LOG_ERROR("DownloadDelegate->willResumeWithResponse failed");
409 }
410
411 void WebDownload::didReceiveData(CFIndex length)
412 {
413 #ifndef NDEBUG
414     m_received += length;
415     WallTime current = WallTime::now();
416     if ((current - m_dataTime) > 2_s)
417         LOG(Download, "DOWNLOAD - %p hanged for %.3f seconds - Received %i bytes for a total of %i", this, (current - m_dataTime).seconds(), length, m_received);
418     m_dataTime = current;
419 #endif
420     if (FAILED(m_delegate->didReceiveDataOfLength(this, length)))
421         LOG_ERROR("DownloadDelegate->didReceiveData failed");
422 }
423
424 bool WebDownload::shouldDecodeDataOfMIMEType(CFStringRef mimeType)
425 {
426     BOOL result;
427     if (FAILED(m_delegate->shouldDecodeSourceDataOfMIMEType(this, BString(mimeType), &result))) {
428         LOG_ERROR("DownloadDelegate->shouldDecodeSourceDataOfMIMEType failed");
429         return false;
430     }
431     return !!result;
432 }
433
434 void WebDownload::decideDestinationWithSuggestedObjectName(CFStringRef name)
435 {    
436     if (FAILED(m_delegate->decideDestinationWithSuggestedFilename(this, BString(name))))
437         LOG_ERROR("DownloadDelegate->decideDestinationWithSuggestedObjectName failed");
438 }
439
440 void WebDownload::didCreateDestination(CFURLRef destination)
441 {
442     // The concept of the ".download bundle" is internal to the WebDownload, so therefore
443     // we try to mask the delegate from its existence as much as possible by telling it the final
444     // destination was created, when in reality the bundle was created
445
446     String createdDestination = MarshallingHelpers::FileCFURLRefToPathString(destination);
447
448     // At this point in receiving CFURLDownload callbacks, we should definitely have the bundle path stored locally
449     // and it should match with the file that CFURLDownload created
450     ASSERT(createdDestination == m_bundlePath);
451     // And we should also always have the final-destination stored
452     ASSERT(!m_destination.isEmpty());
453
454     BString path(m_destination);
455     if (FAILED(m_delegate->didCreateDestination(this, path)))
456         LOG_ERROR("DownloadDelegate->didCreateDestination failed");
457 }
458
459 void WebDownload::didFinish()
460 {
461 #ifndef NDEBUG
462     LOG(Download, "DOWNLOAD - Finished %p after %i bytes and %.3f seconds", this, m_received, (WallTime::now() - m_startTime).seconds());
463 #endif
464
465     ASSERT(!m_bundlePath.isEmpty() && !m_destination.isEmpty());
466     LOG(Download, "WebDownload - Moving file from bundle %s to destination %s", m_bundlePath.ascii().data(), m_destination.ascii().data());
467
468     // We try to rename the bundle to the final file name.  If that fails, we give the delegate one more chance to chose
469     // the final file name, then we just leave it
470     if (!MoveFileEx(m_bundlePath.charactersWithNullTermination().data(), m_destination.charactersWithNullTermination().data(), 0)) {
471         LOG_ERROR("Failed to move bundle %s to %s on completion\nError - %i", m_bundlePath.ascii().data(), m_destination.ascii().data(), GetLastError());
472         
473         bool reportBundlePathAsFinalPath = true;
474
475         BString destinationBSTR(m_destination);
476         if (FAILED(m_delegate->decideDestinationWithSuggestedFilename(this, destinationBSTR)))
477             LOG_ERROR("delegate->decideDestinationWithSuggestedFilename() failed");
478
479         // The call to m_delegate->decideDestinationWithSuggestedFilename() should have changed our destination, so we'll try the move
480         // one last time.
481         if (!m_destination.isEmpty())
482             if (MoveFileEx(m_bundlePath.charactersWithNullTermination().data(), m_destination.charactersWithNullTermination().data(), 0))
483                 reportBundlePathAsFinalPath = false;
484
485         // We either need to tell the delegate our final filename is the bundle filename, or is the file name they just told us to use
486         if (reportBundlePathAsFinalPath) {
487             BString bundleBSTR(m_bundlePath);
488             m_delegate->didCreateDestination(this, bundleBSTR);
489         } else {
490             BString finalDestinationBSTR = BString(m_destination);
491             m_delegate->didCreateDestination(this, finalDestinationBSTR);
492         }
493     }
494
495     // It's extremely likely the call to delegate->didFinish() will deref this, so lets not let that cause our destruction just yet
496     COMPtr<WebDownload> protect = this;
497     if (FAILED(m_delegate->didFinish(this)))
498         LOG_ERROR("DownloadDelegate->didFinish failed");
499
500     m_download = 0;
501 }
502
503 void WebDownload::didFail(CFErrorRef error)
504 {
505     COMPtr<WebError> webError(AdoptCOM, WebError::createInstance(ResourceError(error)));
506     if (FAILED(m_delegate->didFailWithError(this, webError.get())))
507         LOG_ERROR("DownloadDelegate->didFailWithError failed");
508 }
509
510 // CFURLDownload Callbacks ----------------------------------------------------------------
511 void didStartCallback(CFURLDownloadRef, const void *clientInfo)
512 { ((WebDownload*)clientInfo)->didStart(); }
513
514 CFURLRequestRef willSendRequestCallback(CFURLDownloadRef, CFURLRequestRef request, CFURLResponseRef redirectionResponse, const void *clientInfo)
515 { return ((WebDownload*)clientInfo)->willSendRequest(request, redirectionResponse); }
516
517 void didReceiveAuthenticationChallengeCallback(CFURLDownloadRef, CFURLAuthChallengeRef challenge, const void *clientInfo)
518 { ((WebDownload*)clientInfo)->didReceiveAuthenticationChallenge(challenge); }
519
520 void didReceiveResponseCallback(CFURLDownloadRef, CFURLResponseRef response, const void *clientInfo)
521 { ((WebDownload*)clientInfo)->didReceiveResponse(response); }
522
523 void willResumeWithResponseCallback(CFURLDownloadRef, CFURLResponseRef response, UInt64 startingByte, const void *clientInfo)
524 { ((WebDownload*)clientInfo)->willResumeWithResponse(response, startingByte); }
525
526 void didReceiveDataCallback(CFURLDownloadRef, CFIndex length, const void *clientInfo)
527 { ((WebDownload*)clientInfo)->didReceiveData(length); }
528
529 Boolean shouldDecodeDataOfMIMETypeCallback(CFURLDownloadRef, CFStringRef encodingType, const void *clientInfo)
530 { return ((WebDownload*)clientInfo)->shouldDecodeDataOfMIMEType(encodingType); }
531
532 void decideDestinationWithSuggestedObjectNameCallback(CFURLDownloadRef, CFStringRef objectName, const void *clientInfo)
533 { ((WebDownload*)clientInfo)->decideDestinationWithSuggestedObjectName(objectName); }
534
535 void didCreateDestinationCallback(CFURLDownloadRef, CFURLRef path, const void *clientInfo)
536 { ((WebDownload*)clientInfo)->didCreateDestination(path); }
537
538 void didFinishCallback(CFURLDownloadRef, const void *clientInfo)
539 { ((WebDownload*)clientInfo)->didFinish(); }
540
541 void didFailCallback(CFURLDownloadRef, CFErrorRef error, const void *clientInfo)
542 { ((WebDownload*)clientInfo)->didFail(error); }