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