[Curl] Unable to download files.
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 1 Jun 2013 05:41:42 +0000 (05:41 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 1 Jun 2013 05:41:42 +0000 (05:41 +0000)
https://bugs.webkit.org/show_bug.cgi?id=116150

Patch by peavo@outlook.com <peavo@outlook.com> on 2013-05-31
Reviewed by Brent Fulgham.

Implemented basic download functionality for Curl.

Source/WebCore:

* WebCore.vcproj/WebCore.vcproj: Added file.
* WebCore.vcxproj/WebCore.vcxproj: Added file.
* WebCore.vcxproj/WebCore.vcxproj.filters: Added file.
* platform/network/curl/CurlDownload.h: Added.
* platform/network/curl/CurlDownload.cpp: Added.

Source/WebKit/win:

* WebDownload.h:
* WebDownloadCurl.cpp:
(WebDownload::init):
(WebDownload::start):
(WebDownload::cancel):
(WebDownload::deletesFileUponFailure):
(WebDownload::setDeletesFileUponFailure):
(WebDownload::setDestination):
(WebDownload::useCredential):
(WebDownload::didReceiveResponse):
(WebDownload::didReceiveDataOfLength):
(WebDownload::didFinish):
(WebDownload::didFail):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@151067 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/WebCore.vcproj/WebCore.vcproj
Source/WebCore/WebCore.vcxproj/WebCore.vcxproj
Source/WebCore/WebCore.vcxproj/WebCore.vcxproj.filters
Source/WebCore/platform/network/curl/CurlDownload.cpp [new file with mode: 0644]
Source/WebCore/platform/network/curl/CurlDownload.h [new file with mode: 0644]
Source/WebKit/win/ChangeLog
Source/WebKit/win/WebDownload.h
Source/WebKit/win/WebDownloadCurl.cpp

index 977d4c2..3b2e2ce 100644 (file)
@@ -1,3 +1,18 @@
+2013-05-31  peavo@outlook.com  <peavo@outlook.com>
+
+        [Curl] Unable to download files.
+        https://bugs.webkit.org/show_bug.cgi?id=116150
+
+        Reviewed by Brent Fulgham.
+
+        Implemented basic download functionality for Curl.
+
+        * WebCore.vcproj/WebCore.vcproj: Added file.
+        * WebCore.vcxproj/WebCore.vcxproj: Added file.
+        * WebCore.vcxproj/WebCore.vcxproj.filters: Added file.
+        * platform/network/curl/CurlDownload.h: Added.
+        * platform/network/curl/CurlDownload.cpp: Added.
+
 2013-05-31  Anders Carlsson  <andersca@apple.com>
 
         Take the normal NSView code path when drawing into an inclusive layer
index b34195d..bf0938f 100755 (executable)
                                                </FileConfiguration>
                                        </File>
                                        <File
+                                               RelativePath="..\platform\network\curl\CurlDownload.cpp"
+                                               >
+                                               <FileConfiguration
+                                                       Name="Debug|Win32"
+                                                       ExcludedFromBuild="true"
+                                                       >
+                                                       <Tool
+                                                               Name="VCCLCompilerTool"
+                                                       />
+                                               </FileConfiguration>
+                                               <FileConfiguration
+                                                       Name="Release|Win32"
+                                                       ExcludedFromBuild="true"
+                                                       >
+                                                       <Tool
+                                                               Name="VCCLCompilerTool"
+                                                       />
+                                               </FileConfiguration>
+                                               <FileConfiguration
+                                                       Name="Debug_All|Win32"
+                                                       ExcludedFromBuild="true"
+                                                       >
+                                                       <Tool
+                                                               Name="VCCLCompilerTool"
+                                                       />
+                                               </FileConfiguration>
+                                               <FileConfiguration
+                                                       Name="Production|Win32"
+                                                       ExcludedFromBuild="true"
+                                                       >
+                                                       <Tool
+                                                               Name="VCCLCompilerTool"
+                                                       />
+                                               </FileConfiguration>
+                                       </File>
+                                       <File
                                                RelativePath="..\platform\network\curl\DNSCurl.cpp"
                                                >
                                                <FileConfiguration
index 108514d..c0c2e50 100644 (file)
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Production|Win32'">true</ExcludedFromBuild>
     </ClCompile>
+    <ClCompile Include="..\platform\network\curl\CurlDownload.cpp">
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='DebugSuffix|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Production|Win32'">true</ExcludedFromBuild>
+    </ClCompile>
     <ClCompile Include="..\platform\network\curl\DNSCurl.cpp">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='DebugSuffix|Win32'">true</ExcludedFromBuild>
index fbcb1b5..03e3d8a 100644 (file)
     <ClCompile Include="..\platform\network\curl\CredentialStorageCurl.cpp">
       <Filter>platform\network\curl</Filter>
     </ClCompile>
+    <ClCompile Include="..\platform\network\curl\CurlDownload.cpp">
+      <Filter>platform\network\curl</Filter>
+    </ClCompile>
     <ClCompile Include="..\platform\network\curl\DNSCurl.cpp">
       <Filter>platform\network\curl</Filter>
     </ClCompile>
diff --git a/Source/WebCore/platform/network/curl/CurlDownload.cpp b/Source/WebCore/platform/network/curl/CurlDownload.cpp
new file mode 100644 (file)
index 0000000..b64ed21
--- /dev/null
@@ -0,0 +1,418 @@
+/*
+ * Copyright (C) 2013 Apple Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include "config.h"
+#include "CurlDownload.h"
+
+#include <WebCore/HTTPParsers.h>
+#include <WebCore/MainThreadTask.h>
+
+#include <wtf/MainThread.h>
+#include <wtf/text/CString.h>
+
+using namespace WebCore;
+
+template<> struct CrossThreadCopierBase<false, false, CurlDownload*> : public CrossThreadCopierPassThrough<CurlDownload*> {
+};
+
+// CurlDownloadManager -------------------------------------------------------------------
+
+CurlDownloadManager::CurlDownloadManager()
+: m_threadId(0)
+, m_curlMultiHandle(0)
+, m_activeDownloadCount(0)
+, m_runThread(false)
+{
+    curl_global_init(CURL_GLOBAL_ALL);
+    m_curlMultiHandle = curl_multi_init();
+}
+
+CurlDownloadManager::~CurlDownloadManager()
+{
+    stopThread();
+    curl_multi_cleanup(m_curlMultiHandle);
+    curl_global_cleanup();
+}
+
+bool CurlDownloadManager::add(CURL* curlHandle)
+{
+    MutexLocker locker(m_mutex);
+
+    m_pendingHandleList.append(curlHandle);
+    startThreadIfNeeded();
+
+    return true;
+}
+
+bool CurlDownloadManager::remove(CURL* curlHandle)
+{
+    MutexLocker locker(m_mutex);
+
+    m_removedHandleList.append(curlHandle);
+
+    return true;
+}
+
+int CurlDownloadManager::getActiveDownloadCount() const
+{
+    return m_activeDownloadCount;
+}
+
+int CurlDownloadManager::getPendingDownloadCount() const
+{
+    MutexLocker locker(m_mutex);
+    return m_pendingHandleList.size();
+}
+
+void CurlDownloadManager::startThreadIfNeeded()
+{
+    if (!m_runThread) {
+        if (m_threadId)
+            waitForThreadCompletion(m_threadId);
+        m_runThread = true;
+        m_threadId = createThread(downloadThread, this, "downloadThread");
+    }
+}
+
+void CurlDownloadManager::stopThread()
+{
+    m_runThread = false;
+
+    if (m_threadId) {
+        waitForThreadCompletion(m_threadId);
+        m_threadId = 0;
+    }
+}
+
+void CurlDownloadManager::stopThreadIfIdle()
+{
+    MutexLocker locker(m_mutex);
+
+    if (!getActiveDownloadCount() && !getPendingDownloadCount())
+        setRunThread(false);
+}
+
+void CurlDownloadManager::updateHandleList()
+{
+    MutexLocker locker(m_mutex);
+
+    // Add pending curl easy handles to multi list 
+    int size = m_pendingHandleList.size();
+    for (int i = 0; i < size; i++) {
+        CURLMcode retval = curl_multi_add_handle(m_curlMultiHandle, m_pendingHandleList[0]);
+
+        if (retval == CURLM_OK)
+            m_pendingHandleList.remove(0);
+    }
+
+    // Remove curl easy handles from multi list 
+    size = m_removedHandleList.size();
+    for (int i = 0; i < size; i++) {
+        CURLMcode retval = curl_multi_remove_handle(m_curlMultiHandle, m_removedHandleList[0]);
+
+        if (retval == CURLM_OK)
+            m_removedHandleList.remove(0);
+    }
+}
+
+void CurlDownloadManager::downloadThread(void* data)
+{
+    CurlDownloadManager* downloadManager = reinterpret_cast<CurlDownloadManager*>(data);
+
+    while (downloadManager->runThread()) {
+
+        downloadManager->updateHandleList();
+
+        // Retry 'select' if it was interrupted by a process signal.
+        int rc = 0;
+        do {
+            fd_set fdread;
+            fd_set fdwrite;
+            fd_set fdexcep;
+
+            int maxfd = 0;
+
+            const int selectTimeoutMS = 5;
+
+            struct timeval timeout;
+            timeout.tv_sec = 0;
+            timeout.tv_usec = selectTimeoutMS * 1000; // select waits microseconds
+
+            FD_ZERO(&fdread);
+            FD_ZERO(&fdwrite);
+            FD_ZERO(&fdexcep);
+            curl_multi_fdset(downloadManager->getMultiHandle(), &fdread, &fdwrite, &fdexcep, &maxfd);
+            // When the 3 file descriptors are empty, winsock will return -1
+            // and bail out, stopping the file download. So make sure we
+            // have valid file descriptors before calling select.
+            if (maxfd >= 0)
+                rc = ::select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &timeout);
+        } while (rc == -1 && errno == EINTR);
+
+        while (curl_multi_perform(downloadManager->getMultiHandle(), &downloadManager->m_activeDownloadCount) == CURLM_CALL_MULTI_PERFORM) { }
+
+        int messagesInQueue = 0;
+        CURLMsg* msg = curl_multi_info_read(downloadManager->getMultiHandle(), &messagesInQueue);
+
+        if (!msg)
+            continue;
+
+        CurlDownload* download = 0;
+        CURLcode err = curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &download);
+
+        if (msg->msg == CURLMSG_DONE) {
+            if (msg->data.result == CURLE_OK)
+                callOnMainThread<CurlDownload*, CurlDownload*>(CurlDownload::downloadFinishedCallback, download);
+            else
+                callOnMainThread<CurlDownload*, CurlDownload*>(CurlDownload::downloadFailedCallback, download);
+
+            curl_multi_remove_handle(downloadManager->getMultiHandle(), msg->easy_handle);
+        }
+
+        downloadManager->stopThreadIfIdle();
+    }
+}
+
+// CurlDownload --------------------------------------------------------------------------
+
+CurlDownloadManager CurlDownload::m_downloadManager;
+
+CurlDownload::CurlDownload()
+: m_curlHandle(0)
+, m_url(0)
+, m_tempHandle(invalidPlatformFileHandle)
+, m_deletesFileUponFailure(false)
+, m_listener(0)
+{
+}
+
+CurlDownload::~CurlDownload()
+{
+    MutexLocker locker(m_mutex);
+
+    if (m_curlHandle)
+        curl_easy_cleanup(m_curlHandle);
+
+    if (m_url)
+        fastFree(m_url);
+
+    closeFile();
+    moveFileToDestination();
+}
+
+void CurlDownload::init(CurlDownloadListener* listener, const KURL& url)
+{
+    if (!listener)
+        return;
+
+    MutexLocker locker(m_mutex);
+
+    m_curlHandle = curl_easy_init();
+
+    String urlStr = url.string();
+    m_url = fastStrDup(urlStr.latin1().data());
+
+    curl_easy_setopt(m_curlHandle, CURLOPT_URL, m_url);
+    curl_easy_setopt(m_curlHandle, CURLOPT_PRIVATE, this);
+    curl_easy_setopt(m_curlHandle, CURLOPT_WRITEFUNCTION, writeCallback);
+    curl_easy_setopt(m_curlHandle, CURLOPT_WRITEDATA, this);
+    curl_easy_setopt(m_curlHandle, CURLOPT_HEADERFUNCTION, headerCallback);
+    curl_easy_setopt(m_curlHandle, CURLOPT_WRITEHEADER, this);
+    curl_easy_setopt(m_curlHandle, CURLOPT_FOLLOWLOCATION, 1);
+    curl_easy_setopt(m_curlHandle, CURLOPT_MAXREDIRS, 10);
+    curl_easy_setopt(m_curlHandle, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
+
+    m_listener = listener;
+}
+
+bool CurlDownload::start()
+{
+    return m_downloadManager.add(m_curlHandle);
+}
+
+bool CurlDownload::cancel()
+{
+    return m_downloadManager.remove(m_curlHandle);
+}
+
+String CurlDownload::getTempPath() const
+{
+    MutexLocker locker(m_mutex);
+    return m_tempPath;
+}
+
+String CurlDownload::getUrl() const
+{
+    MutexLocker locker(m_mutex);
+    return String(m_url);
+}
+
+ResourceResponse CurlDownload::getResponse() const
+{
+    MutexLocker locker(m_mutex);
+    return m_response;
+}
+
+void CurlDownload::closeFile()
+{
+    MutexLocker locker(m_mutex);
+
+    if (m_tempHandle) {
+        WebCore::closeFile(m_tempHandle);
+        m_tempHandle = invalidPlatformFileHandle;
+    }
+}
+
+void CurlDownload::moveFileToDestination()
+{
+    if (m_destination.isEmpty())
+        return;
+
+    ::MoveFile(m_tempPath.charactersWithNullTermination(), m_destination.charactersWithNullTermination());
+}
+
+void CurlDownload::didReceiveHeader(const String& header)
+{
+    MutexLocker locker(m_mutex);
+
+    if (header == "\r\n" || header == "\n") {
+
+        long httpCode = 0;
+        CURLcode err = curl_easy_getinfo(m_curlHandle, CURLINFO_RESPONSE_CODE, &httpCode);
+
+        if (httpCode >= 200 && httpCode < 300) {
+            const char* url = 0;
+            err = curl_easy_getinfo(m_curlHandle, CURLINFO_EFFECTIVE_URL, &url);
+            m_response.setURL(KURL(ParsedURLString, url));
+
+            m_response.setMimeType(extractMIMETypeFromMediaType(m_response.httpHeaderField("Content-Type")));
+            m_response.setTextEncodingName(extractCharsetFromMediaType(m_response.httpHeaderField("Content-Type")));
+            m_response.setSuggestedFilename(filenameFromHTTPContentDisposition(m_response.httpHeaderField("Content-Disposition")));
+
+            callOnMainThread<CurlDownload*, CurlDownload*>(receivedResponseCallback, this);
+        }
+    } else {
+        int splitPos = header.find(":");
+        if (splitPos != -1)
+            m_response.setHTTPHeaderField(header.left(splitPos), header.substring(splitPos+1).stripWhiteSpace());
+    }
+}
+
+void CurlDownload::didReceiveData(void* data, int size)
+{
+    MutexLocker locker(m_mutex);
+
+    callOnMainThread<CurlDownload*, CurlDownload*, int, int>(receivedDataCallback, this, size);
+
+    if (m_tempPath.isEmpty())
+        m_tempPath = openTemporaryFile("download", m_tempHandle);
+
+    if (m_tempHandle != invalidPlatformFileHandle) {
+        const char* fileData = static_cast<const char*>(data);
+        writeToFile(m_tempHandle, fileData, size);
+    }
+}
+
+void CurlDownload::didReceiveResponse()
+{
+    if (m_listener)
+        m_listener->didReceiveResponse();
+}
+
+void CurlDownload::didReceiveDataOfLength(int size)
+{
+    if (m_listener)
+        m_listener->didReceiveDataOfLength(size);
+}
+
+void CurlDownload::didFinish()
+{
+    closeFile();
+    moveFileToDestination();
+
+    if (m_listener)
+        m_listener->didFinish();
+}
+
+void CurlDownload::didFail()
+{
+    MutexLocker locker(m_mutex);
+
+    closeFile();
+
+    if (m_deletesFileUponFailure)
+        deleteFile(m_tempPath);
+
+    if (m_listener)
+        m_listener->didFail();
+}
+
+size_t CurlDownload::writeCallback(void* ptr, size_t size, size_t nmemb, void* data)
+{
+    size_t totalSize = size * nmemb;
+    CurlDownload* download = reinterpret_cast<CurlDownload*>(data);
+
+    if (download)
+        download->didReceiveData(ptr, totalSize);
+
+    return totalSize;
+}
+
+size_t CurlDownload::headerCallback(char* ptr, size_t size, size_t nmemb, void* data)
+{
+    size_t totalSize = size * nmemb;
+    CurlDownload* download = reinterpret_cast<CurlDownload*>(data);
+
+    String header(static_cast<const char*>(ptr), totalSize);
+
+    if (download)
+        download->didReceiveHeader(header);
+
+    return totalSize;
+}
+
+void CurlDownload::downloadFinishedCallback(CurlDownload* download)
+{
+    if (download)
+        download->didFinish();
+}
+
+void CurlDownload::downloadFailedCallback(CurlDownload* download)
+{
+    if (download)
+        download->didFail();
+}
+
+void CurlDownload::receivedDataCallback(CurlDownload* download, int size)
+{
+    if (download)
+        download->didReceiveDataOfLength(size);
+}
+
+void CurlDownload::receivedResponseCallback(CurlDownload* download)
+{
+    if (download)
+        download->didReceiveResponse();
+}
diff --git a/Source/WebCore/platform/network/curl/CurlDownload.h b/Source/WebCore/platform/network/curl/CurlDownload.h
new file mode 100644 (file)
index 0000000..110b962
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2013 Apple Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#ifndef CurlDownload_h
+#define CurlDownload_h
+
+#include <WebCore/FileSystem.h>
+#include <WebCore/ResourceResponse.h>
+
+#if PLATFORM(WIN)
+#include <windows.h>
+#include <winsock2.h>
+#endif
+
+#include <curl/curl.h>
+
+class CurlDownloadManager {
+public:
+    CurlDownloadManager();
+    ~CurlDownloadManager();
+
+    bool add(CURL* curlHandle);
+    bool remove(CURL* curlHandle);
+
+    int getActiveDownloadCount() const;
+    int getPendingDownloadCount() const;
+
+private:
+    void startThreadIfNeeded();
+    void stopThread();
+    void stopThreadIfIdle();
+
+    void updateHandleList();
+
+    CURLM* getMultiHandle() const { return m_curlMultiHandle; }
+
+    bool runThread() const { return m_runThread; }
+    void setRunThread(bool runThread) { m_runThread = runThread; }
+
+    static void downloadThread(void* data);
+
+    ThreadIdentifier m_threadId;
+    CURLM* m_curlMultiHandle;
+    int m_activeDownloadCount;
+    Vector<CURL*> m_pendingHandleList;
+    Vector<CURL*> m_removedHandleList;
+    mutable Mutex m_mutex;
+    bool m_runThread;
+};
+
+class CurlDownloadListener {
+public:
+    virtual void didReceiveResponse() { }
+    virtual void didReceiveDataOfLength(int size) { }
+    virtual void didFinish() { }
+    virtual void didFail() { }
+};
+
+class CurlDownload {
+public:
+    CurlDownload();
+    ~CurlDownload();
+
+    void init(CurlDownloadListener*, const WebCore::KURL&);
+    bool start();
+    bool cancel();
+
+    String getTempPath() const;
+    String getUrl() const;
+    WebCore::ResourceResponse getResponse() const;
+
+    bool deletesFileUponFailure() const { return m_deletesFileUponFailure; }
+    void setDeletesFileUponFailure(bool deletesFileUponFailure) { m_deletesFileUponFailure = deletesFileUponFailure; }
+
+    void setDestination(const String& destination) { m_destination = destination; }
+
+private:
+    void closeFile();
+    void moveFileToDestination();
+
+    // Called on download thread.
+    void didReceiveHeader(const String& header);
+    void didReceiveData(void* data, int size);
+
+    // Called on main thread.
+    void didReceiveResponse();
+    void didReceiveDataOfLength(int size);
+    void didFinish();
+    void didFail();
+
+    static size_t writeCallback(void* ptr, size_t, size_t nmemb, void* data);
+    static size_t headerCallback(char* ptr, size_t, size_t nmemb, void* data);
+
+    static void downloadFinishedCallback(CurlDownload*);
+    static void downloadFailedCallback(CurlDownload*);
+    static void receivedDataCallback(CurlDownload*, int size);
+    static void receivedResponseCallback(CurlDownload*);
+
+    CURL* m_curlHandle;
+    char* m_url;
+    String m_tempPath;
+    String m_destination;
+    WebCore::PlatformFileHandle m_tempHandle;
+    WebCore::ResourceResponse m_response;
+    bool m_deletesFileUponFailure;
+    mutable Mutex m_mutex;
+    CurlDownloadListener *m_listener;
+
+    static CurlDownloadManager m_downloadManager;
+
+    friend class CurlDownloadManager;
+};
+
+#endif
index eac962e..6ce8734 100644 (file)
@@ -1,3 +1,26 @@
+2013-05-31  peavo@outlook.com  <peavo@outlook.com>
+
+        [Curl] Unable to download files.
+        https://bugs.webkit.org/show_bug.cgi?id=116150
+
+        Reviewed by Brent Fulgham.
+
+        Implemented basic download functionality for Curl.
+
+        * WebDownload.h:
+        * WebDownloadCurl.cpp:
+        (WebDownload::init):
+        (WebDownload::start):
+        (WebDownload::cancel):
+        (WebDownload::deletesFileUponFailure):
+        (WebDownload::setDeletesFileUponFailure):
+        (WebDownload::setDestination):
+        (WebDownload::useCredential):
+        (WebDownload::didReceiveResponse):
+        (WebDownload::didReceiveDataOfLength):
+        (WebDownload::didFinish):
+        (WebDownload::didFail):
+
 2013-05-29  Brent Fulgham  <bfulgham@apple.com>
 
         [Windows] Be consistent with allocation/deallocation behavior
index d9ca277..679b9d0 100644 (file)
@@ -33,6 +33,8 @@
 
 #if USE(CFNETWORK)
 #include <CFNetwork/CFURLDownloadPriv.h>
+#elif USE(CURL)
+#include <WebCore/CurlDownload.h>
 #endif
 
 namespace WebCore {
@@ -42,7 +44,12 @@ namespace WebCore {
     class ResourceResponse;
 }
 
-class WebDownload : public IWebDownload, public IWebURLAuthenticationChallengeSender
+class WebDownload
+: public IWebDownload
+, public IWebURLAuthenticationChallengeSender
+#if USE(CURL)
+, public CurlDownloadListener
+#endif
 {
 public:
     static WebDownload* createInstance(const WebCore::KURL&, IWebDownloadDelegate*);
@@ -119,6 +126,11 @@ public:
     void didCreateDestination(CFURLRef);
     void didFinish();
     void didFail(CFErrorRef);
+#elif USE(CURL)
+    virtual void didReceiveResponse();
+    virtual void didReceiveDataOfLength(int size);
+    virtual void didFinish();
+    virtual void didFail();
 #endif
 
 protected:
@@ -128,6 +140,8 @@ protected:
     WTF::String m_bundlePath;
 #if USE(CFNETWORK)
     RetainPtr<CFURLDownloadRef> m_download;
+#elif USE(CURL)
+    CurlDownload m_download;
 #endif
     COMPtr<IWebMutableURLRequest> m_request;
     COMPtr<IWebDownloadDelegate> m_delegate;
index 7eb3ae4..6d2c8e0 100644 (file)
@@ -62,7 +62,9 @@ void WebDownload::init(ResourceHandle* handle, const ResourceRequest& request, c
 
 void WebDownload::init(const KURL& url, IWebDownloadDelegate* delegate)
 {
-   notImplemented();
+    m_delegate = delegate;
+
+    m_download.init(this, url);
 }
 
 // IWebDownload -------------------------------------------------------------------
@@ -85,14 +87,21 @@ HRESULT STDMETHODCALLTYPE WebDownload::initToResumeWithBundle(
 
 HRESULT STDMETHODCALLTYPE WebDownload::start()
 {
-   notImplemented();
-   return E_FAIL;
+    if (!m_download.start())
+        return E_FAIL;
+
+    if (m_delegate)
+        m_delegate->didBegin(this);
+
+    return S_OK;
 }
 
 HRESULT STDMETHODCALLTYPE WebDownload::cancel()
 {
-   notImplemented();
-   return E_FAIL;
+    if (!m_download.cancel())
+        return E_FAIL;
+
+    return S_OK;
 }
 
 HRESULT STDMETHODCALLTYPE WebDownload::cancelForResume()
@@ -104,23 +113,25 @@ HRESULT STDMETHODCALLTYPE WebDownload::cancelForResume()
 HRESULT STDMETHODCALLTYPE WebDownload::deletesFileUponFailure(
         /* [out, retval] */ BOOL* result)
 {
-   notImplemented();
-   return E_FAIL;
+    *result = m_download.deletesFileUponFailure() ? TRUE : FALSE;
+    return S_OK;
 }
 
 HRESULT STDMETHODCALLTYPE WebDownload::setDeletesFileUponFailure(
         /* [in] */ BOOL deletesFileUponFailure)
 {
-   notImplemented();
-   return E_FAIL;
+    m_download.setDeletesFileUponFailure(deletesFileUponFailure);
+    return S_OK;
 }
 
 HRESULT STDMETHODCALLTYPE WebDownload::setDestination(
         /* [in] */ BSTR path, 
         /* [in] */ BOOL allowOverwrite)
 {
-   notImplemented();
-   return E_FAIL;
+    size_t len = wcslen(path);
+    m_destination = String(path, len);
+    m_download.setDestination(m_destination);
+    return S_OK;
 }
 
 // IWebURLAuthenticationChallengeSender -------------------------------------------------------------------
@@ -146,3 +157,44 @@ HRESULT STDMETHODCALLTYPE WebDownload::useCredential(
    notImplemented();
    return E_FAIL;
 }
+
+void WebDownload::didReceiveResponse()
+{
+    COMPtr<WebDownload> protect = this;
+
+    if (m_delegate) {
+        ResourceResponse response = m_download.getResponse();
+        COMPtr<WebURLResponse> webResponse(AdoptCOM, WebURLResponse::createInstance(response));
+        m_delegate->didReceiveResponse(this, webResponse.get());
+
+        String suggestedFilename = response.suggestedFilename();
+        if (suggestedFilename.isEmpty())
+            suggestedFilename = pathGetFileName(response.url().string());
+        BString suggestedFilenameBSTR(suggestedFilename.characters(), suggestedFilename.length());
+        m_delegate->decideDestinationWithSuggestedFilename(this, suggestedFilenameBSTR);
+    }
+}
+
+void WebDownload::didReceiveDataOfLength(int size)
+{
+    COMPtr<WebDownload> protect = this;
+
+    if (m_delegate)
+        m_delegate->didReceiveDataOfLength(this, size);
+}
+
+void WebDownload::didFinish()
+{
+    COMPtr<WebDownload> protect = this;
+
+    if (m_delegate)
+        m_delegate->didFinish(this);
+}
+
+void WebDownload::didFail()
+{
+    COMPtr<WebDownload> protect = this;
+
+    if (m_delegate)
+        m_delegate->didFailWithError(this, 0);
+}