|
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 #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); } |