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