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