1 /* 2 * Copyright (C) 2010, 2011 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include "config.h" 27 #include "Download.h" 28 29 #include "DataReference.h" 30 31 #pragma warning(push, 0) 32 #include <WebCore/DownloadBundle.h> 33 #include <WebCore/LoaderRunLoopCF.h> 34 #include <WebCore/NotImplemented.h> 35 #include <WebCore/ResourceError.h> 36 #include <WebCore/ResourceHandle.h> 37 #include <WebCore/ResourceResponse.h> 38 #pragma warning(pop) 39 40 using namespace WebCore; 41 42 namespace WebKit { 43 44 // CFURLDownload Callbacks ---------------------------------------------------------------- 45 static void didStartCallback(CFURLDownloadRef download, const void* clientInfo); 46 static CFURLRequestRef willSendRequestCallback(CFURLDownloadRef download, CFURLRequestRef request, CFURLResponseRef redirectionResponse, const void* clientInfo); 47 static void didReceiveAuthenticationChallengeCallback(CFURLDownloadRef download, CFURLAuthChallengeRef challenge, const void* clientInfo); 48 static void didReceiveResponseCallback(CFURLDownloadRef download, CFURLResponseRef response, const void* clientInfo); 49 static void willResumeWithResponseCallback(CFURLDownloadRef download, CFURLResponseRef response, UInt64 startingByte, const void* clientInfo); 50 static void didReceiveDataCallback(CFURLDownloadRef download, CFIndex length, const void* clientInfo); 51 static Boolean shouldDecodeDataOfMIMETypeCallback(CFURLDownloadRef download, CFStringRef encodingType, const void* clientInfo); 52 static void decideDestinationWithSuggestedObjectNameCallback(CFURLDownloadRef download, CFStringRef objectName, const void* clientInfo); 53 static void didCreateDestinationCallback(CFURLDownloadRef download, CFURLRef path, const void* clientInfo); 54 static void didFinishCallback(CFURLDownloadRef download, const void* clientInfo); 55 static void didFailCallback(CFURLDownloadRef download, CFErrorRef error, const void* clientInfo); 56 57 void Download::start(WebPage*) 58 { 59 ASSERT(!m_download); 60 61 CFURLRequestRef cfRequest = m_request.cfURLRequest(); 62 63 CFURLDownloadClient client = {0, this, 0, 0, 0, didStartCallback, willSendRequestCallback, didReceiveAuthenticationChallengeCallback, 64 didReceiveResponseCallback, willResumeWithResponseCallback, didReceiveDataCallback, shouldDecodeDataOfMIMETypeCallback, 65 decideDestinationWithSuggestedObjectNameCallback, didCreateDestinationCallback, didFinishCallback, didFailCallback}; 66 m_download.adoptCF(CFURLDownloadCreate(0, cfRequest, &client)); 67 68 // FIXME: Allow this to be changed by the client. 69 CFURLDownloadSetDeletesUponFailure(m_download.get(), false); 70 71 CFURLDownloadScheduleWithCurrentMessageQueue(m_download.get()); 72 CFURLDownloadScheduleDownloadWithRunLoop(m_download.get(), loaderRunLoop(), kCFRunLoopDefaultMode); 73 74 CFURLDownloadStart(m_download.get()); 75 } 76 77 void Download::startWithHandle(WebPage*, ResourceHandle* handle, const ResourceRequest& initialRequest, const ResourceResponse& response) 78 { 79 ASSERT(!m_download); 80 81 CFURLConnectionRef connection = handle->connection(); 82 if (!connection) 83 return; 84 85 CFURLDownloadClient client = {0, this, 0, 0, 0, didStartCallback, willSendRequestCallback, didReceiveAuthenticationChallengeCallback, 86 didReceiveResponseCallback, willResumeWithResponseCallback, didReceiveDataCallback, shouldDecodeDataOfMIMETypeCallback, 87 decideDestinationWithSuggestedObjectNameCallback, didCreateDestinationCallback, didFinishCallback, didFailCallback}; 88 89 m_download.adoptCF(CFURLDownloadCreateAndStartWithLoadingConnection(0, connection, initialRequest.cfURLRequest(), response.cfURLResponse(), &client)); 90 91 // It is possible for CFURLDownloadCreateAndStartWithLoadingConnection() to fail if the passed in CFURLConnection is not in a "downloadable state" 92 // However, we should never hit that case 93 if (!m_download) 94 ASSERT_NOT_REACHED(); 95 96 // The CFURLDownload either starts successfully and retains the CFURLConnection, 97 // or it fails to creating and we have a now-useless connection with a dangling ref. 98 // Either way, we need to release the connection to balance out ref counts 99 handle->releaseConnectionForDownload(); 100 CFRelease(connection); 101 } 102 103 void Download::cancel() 104 { 105 ASSERT(m_download); 106 if (!m_download) 107 return; 108 109 CFURLDownloadSetDeletesUponFailure(m_download.get(), false); 110 CFURLDownloadCancel(m_download.get()); 111 112 RetainPtr<CFDataRef> resumeData(AdoptCF, CFURLDownloadCopyResumeData(m_download.get())); 113 if (resumeData) 114 DownloadBundle::appendResumeData(resumeData.get(), m_bundlePath); 115 116 didCancel(CoreIPC::DataReference()); 117 } 118 119 void Download::platformInvalidate() 120 { 121 m_download = nullptr; 122 } 123 124 void Download::didDecideDestination(const String& destination, bool allowOverwrite) 125 { 126 ASSERT(!destination.isEmpty()); 127 if (destination.isEmpty()) 128 return; 129 130 m_allowOverwrite = allowOverwrite; 131 m_destination = destination; 132 m_bundlePath = destination + DownloadBundle::fileExtension(); 133 134 RetainPtr<CFStringRef> bundlePath(AdoptCF, CFStringCreateWithCharactersNoCopy(0, reinterpret_cast<const UniChar*>(m_bundlePath.characters()), m_bundlePath.length(), kCFAllocatorNull)); 135 RetainPtr<CFURLRef> bundlePathURL(AdoptCF, CFURLCreateWithFileSystemPath(0, bundlePath.get(), kCFURLWindowsPathStyle, false)); 136 CFURLDownloadSetDestination(m_download.get(), bundlePathURL.get(), allowOverwrite); 137 } 138 139 // CFURLDownload Callbacks ---------------------------------------------------------------- 140 static Download* downloadFromClientInfo(const void* clientInfo) 141 { 142 return reinterpret_cast<Download*>(const_cast<void*>(clientInfo)); 143 } 144 145 void didStartCallback(CFURLDownloadRef, const void* clientInfo) 146 { 147 downloadFromClientInfo(clientInfo)->didStart(); 148 } 149 150 CFURLRequestRef willSendRequestCallback(CFURLDownloadRef, CFURLRequestRef request, CFURLResponseRef redirectionResponse, const void* clientInfo) 151 { 152 // CFNetwork requires us to return a retained request. 153 CFRetain(request); 154 return request; 155 } 156 157 void didReceiveAuthenticationChallengeCallback(CFURLDownloadRef, CFURLAuthChallengeRef challenge, const void* clientInfo) 158 { 159 // FIXME: implement. 160 notImplemented(); 161 } 162 163 void didReceiveResponseCallback(CFURLDownloadRef, CFURLResponseRef response, const void* clientInfo) 164 { 165 downloadFromClientInfo(clientInfo)->didReceiveResponse(ResourceResponse(response)); 166 } 167 168 void willResumeWithResponseCallback(CFURLDownloadRef, CFURLResponseRef response, UInt64 startingByte, const void* clientInfo) 169 { 170 // FIXME: implement. 171 notImplemented(); 172 } 173 174 void didReceiveDataCallback(CFURLDownloadRef, CFIndex length, const void* clientInfo) 175 { 176 downloadFromClientInfo(clientInfo)->didReceiveData(length); 177 } 178 179 Boolean shouldDecodeDataOfMIMETypeCallback(CFURLDownloadRef, CFStringRef encodingType, const void* clientInfo) 180 { 181 return downloadFromClientInfo(clientInfo)->shouldDecodeSourceDataOfMIMEType(encodingType); 182 } 183 184 void decideDestinationWithSuggestedObjectNameCallback(CFURLDownloadRef, CFStringRef objectName, const void* clientInfo) 185 { 186 Download* download = downloadFromClientInfo(clientInfo); 187 bool allowOverwrite; 188 download->decideDestinationWithSuggestedFilename(objectName, allowOverwrite); 189 } 190 191 void didCreateDestinationCallback(CFURLDownloadRef, CFURLRef, const void* clientInfo) 192 { 193 // The concept of the ".download bundle" is internal to the Download, so we try to hide its 194 // existence by reporting the final destination was created, when in reality the bundle was created. 195 196 Download* download = downloadFromClientInfo(clientInfo); 197 download->didCreateDestination(download->destination()); 198 } 199 200 void didFinishCallback(CFURLDownloadRef, const void* clientInfo) 201 { 202 downloadFromClientInfo(clientInfo)->didFinish(); 203 } 204 205 void didFailCallback(CFURLDownloadRef, CFErrorRef error, const void* clientInfo) 206 { 207 CoreIPC::DataReference dataReference(0, 0); 208 downloadFromClientInfo(clientInfo)->didFail(ResourceError(error), dataReference); 209 } 210 211 void Download::receivedCredential(const AuthenticationChallenge& authenticationChallenge, const Credential& credential) 212 { 213 notImplemented(); 214 } 215 216 void Download::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge& authenticationChallenge) 217 { 218 notImplemented(); 219 } 220 221 void Download::receivedCancellation(const AuthenticationChallenge& authenticationChallenge) 222 { 223 notImplemented(); 224 } 225 226 } // namespace WebKit 227