2011-02-10 Martin Robinson <mrobinson@igalia.com>
authormrobinson@webkit.org <mrobinson@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 10 Feb 2011 20:26:58 +0000 (20:26 +0000)
committermrobinson@webkit.org <mrobinson@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 10 Feb 2011 20:26:58 +0000 (20:26 +0000)
        Reviewed by Gustavo Noronha Silva.

        [Soup] ResourceHandleSoup does not ever call didSendData for file uploads
        https://bugs.webkit.org/show_bug.cgi?id=52090

        * platform/gtk/Skipped: Unskip tests which should now be passing.
2011-02-10  Martin Robinson  <mrobinson@igalia.com>

        Reviewed by Gustavo Noronha Silva.

        [Soup] ResourceHandleSoup does not ever call didSendData for file uploads
        https://bugs.webkit.org/show_bug.cgi?id=52090

        Call didSendData as body data is uploaded to the server. This is necessary
        for XHR upload events to function properly.

        * platform/network/ResourceHandleInternal.h:
        (WebCore::ResourceHandleInternal::ResourceHandleInternal): Add two new members.
        One to track the total amount of body data and one to track the total amount of
        body data sent so far.
        * platform/network/soup/ResourceHandleSoup.cpp:
        (WebCore::wroteBodyDataCallback): Added.
        (WebCore::addFormElementsToSoupMessage): Split this out into a helper function.
        Will now now sum the total amount of body data.
        (WebCore::startHttp): Attach a wrote-body-data signal handler.

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

LayoutTests/ChangeLog
LayoutTests/platform/gtk/Skipped
Source/WebCore/ChangeLog
Source/WebCore/platform/network/ResourceHandleInternal.h
Source/WebCore/platform/network/soup/ResourceHandleSoup.cpp

index feb4243..687623f 100644 (file)
@@ -1,3 +1,12 @@
+2011-02-10  Martin Robinson  <mrobinson@igalia.com>
+
+        Reviewed by Gustavo Noronha Silva.
+
+        [Soup] ResourceHandleSoup does not ever call didSendData for file uploads
+        https://bugs.webkit.org/show_bug.cgi?id=52090
+
+        * platform/gtk/Skipped: Unskip tests which should now be passing.
+
 2011-02-10  Robert Hogan  <robert@webkit.org>
 
         Reviewed by Laszlo Gombos.
index d749345..681854f 100644 (file)
@@ -267,7 +267,6 @@ fast/js/i18n-bindings-locale.html
 #   Tests failing
 http/tests/appcache/crash-when-navigating-away-then-back.html
 http/tests/appcache/fallback.html
-http/tests/appcache/foreign-iframe-main.html
 http/tests/appcache/local-content.html
 http/tests/appcache/manifest-redirect-2.html
 http/tests/appcache/offline-access.html
@@ -338,9 +337,6 @@ http/tests/xmlhttprequest/frame-unload-abort-crash.html
 http/tests/xmlhttprequest/response-encoding.html
 http/tests/xmlhttprequest/state-after-network-error.html
 http/tests/xmlhttprequest/supported-xml-content-types.html
-http/tests/xmlhttprequest/upload-onload-event.html
-http/tests/xmlhttprequest/upload-onprogress-event.html
-http/tests/xmlhttprequest/upload-progress-events.html
 http/tests/xmlhttprequest/xml-encoding.html
 http/tests/xmlhttprequest/xmlhttprequest-contenttype-empty.html
 # Tests crashing - https://bugs.webkit.org/show_bug.cgi?id=51104
@@ -2812,11 +2808,11 @@ editing/pasteboard/drop-link.html
 # https://bugs.webkit.org/show_bug.cgi?id=52882
 http/tests/security/drag-drop-same-unique-origin.html
 
-# [Soup] ResourceHandleSoup does not ever call didSendData for file uploads
-# https://bugs.webkit.org/show_bug.cgi?id=52090
-http/tests/local/formdata/upload-events.html
-fast/files/workers/worker-apply-blob-url-to-xhr.html
+# This test crashes: https://bugs.webkit.org/show_bug.cgi?id=54234 
 fast/files/apply-blob-url-to-xhr.html
+# Fails with "Received error from worker: ReferenceError: Can't find variable: revokeObjectURL
+# Should recheck it when the above bug is fixed.
+fast/files/workers/worker-apply-blob-url-to-xhr.html
 
 # [Soup] ResourceHandleSoup does not handle encodedBlobData
 # https://bugs.webkit.org/show_bug.cgi?id=52092
index ee17938..dc441a1 100644 (file)
@@ -1,3 +1,23 @@
+2011-02-10  Martin Robinson  <mrobinson@igalia.com>
+
+        Reviewed by Gustavo Noronha Silva.
+
+        [Soup] ResourceHandleSoup does not ever call didSendData for file uploads
+        https://bugs.webkit.org/show_bug.cgi?id=52090
+
+        Call didSendData as body data is uploaded to the server. This is necessary
+        for XHR upload events to function properly.
+
+        * platform/network/ResourceHandleInternal.h:
+        (WebCore::ResourceHandleInternal::ResourceHandleInternal): Add two new members.
+        One to track the total amount of body data and one to track the total amount of
+        body data sent so far.
+        * platform/network/soup/ResourceHandleSoup.cpp:
+        (WebCore::wroteBodyDataCallback): Added.
+        (WebCore::addFormElementsToSoupMessage): Split this out into a helper function.
+        Will now now sum the total amount of body data.
+        (WebCore::startHttp): Attach a wrote-body-data signal handler.
+
 2011-02-10  Bill Budge  <bbudge@chromium.org>
 
         Reviewed by David Levin.
index 03999b5..5512062 100644 (file)
@@ -115,6 +115,8 @@ namespace WebCore {
             , m_cancelled(false)
             , m_buffer(0)
             , m_total(0)
+            , m_bodySize(0)
+            , m_bodyDataSent(0)
             , m_idleHandler(0)
             , m_gotChunkHandler(0)
 #endif
@@ -193,6 +195,8 @@ namespace WebCore {
         GRefPtr<GCancellable> m_cancellable;
         char* m_buffer;
         gsize m_total;
+        unsigned long m_bodySize;
+        unsigned long m_bodyDataSent;
         guint m_idleHandler;
         RefPtr<NetworkingContext> m_context;
         gulong m_gotChunkHandler;
index 92fc258..3b1f157 100644 (file)
@@ -297,6 +297,25 @@ static void gotHeadersCallback(SoupMessage* msg, gpointer data)
     client->didReceiveResponse(handle.get(), d->m_response);
 }
 
+static void wroteBodyDataCallback(SoupMessage*, SoupBuffer* buffer, gpointer data)
+{
+    RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(data);
+    if (!handle)
+        return;
+
+    ASSERT(buffer);
+    ResourceHandleInternal* internal = handle->getInternal();
+    internal->m_bodyDataSent += buffer->length;
+
+    if (internal->m_cancelled)
+        return;
+    ResourceHandleClient* client = handle->client();
+    if (!client)
+        return;
+
+    client->didSendData(handle.get(), internal->m_bodyDataSent, internal->m_bodySize);
+}
+
 // This callback will not be called if the content sniffer is disabled in startHTTPRequest.
 static void contentSniffedCallback(SoupMessage* msg, const char* sniffedType, GHashTable *params, gpointer data)
 {
@@ -482,6 +501,49 @@ static void sendRequestCallback(GObject* source, GAsyncResult* res, gpointer use
                               G_PRIORITY_DEFAULT, d->m_cancellable.get(), readCallback, 0);
 }
 
+static bool addFormElementsToSoupMessage(SoupMessage* message, const char* contentType, FormData* httpBody, unsigned long& totalBodySize)
+{
+    size_t numElements = httpBody->elements().size();
+    if (numElements < 2) { // No file upload is the most common case.
+        Vector<char> body;
+        httpBody->flatten(body);
+        totalBodySize = body.size();
+        soup_message_set_request(message, contentType, SOUP_MEMORY_COPY, body.data(), body.size());
+        return true;
+    }
+
+    // We have more than one element to upload, and some may be large files,
+    // which we will want to mmap instead of copying into memory
+    soup_message_body_set_accumulate(message->request_body, FALSE);
+    for (size_t i = 0; i < numElements; i++) {
+        const FormDataElement& element = httpBody->elements()[i];
+
+        if (element.m_type == FormDataElement::data) {
+            totalBodySize += element.m_data.size();
+            soup_message_body_append(message->request_body, SOUP_MEMORY_TEMPORARY,
+                                     element.m_data.data(), element.m_data.size());
+            continue;
+        }
+
+        // This technique is inspired by libsoup's simple-httpd test.
+        GOwnPtr<GError> error;
+        CString fileName = fileSystemRepresentation(element.m_filename);
+        GMappedFile* fileMapping = g_mapped_file_new(fileName.data(), false, &error.outPtr());
+        if (error)
+            return false;
+
+        gsize mappedFileSize = g_mapped_file_get_length(fileMapping);
+        totalBodySize += mappedFileSize;
+        SoupBuffer* soupBuffer = soup_buffer_new_with_owner(g_mapped_file_get_contents(fileMapping),
+                                                            mappedFileSize, fileMapping,
+                                                            reinterpret_cast<GDestroyNotify>(g_mapped_file_unref));
+        soup_message_body_append_buffer(message->request_body, soupBuffer);
+        soup_buffer_free(soupBuffer);
+    }
+
+    return true;
+}
+
 static bool startHTTPRequest(ResourceHandle* handle)
 {
     ASSERT(handle);
@@ -520,6 +582,7 @@ static bool startHTTPRequest(ResourceHandle* handle)
 
     g_signal_connect(soupMessage, "restarted", G_CALLBACK(restartedCallback), handle);
     g_signal_connect(soupMessage, "got-headers", G_CALLBACK(gotHeadersCallback), handle);
+    g_signal_connect(soupMessage, "wrote-body-data", G_CALLBACK(wroteBodyDataCallback), handle);
     d->m_gotChunkHandler = g_signal_connect(soupMessage, "got-chunk", G_CALLBACK(gotChunkCallback), handle);
 
     String firstPartyString = request.firstPartyForCookies().string();
@@ -529,54 +592,13 @@ static bool startHTTPRequest(ResourceHandle* handle)
     }
 
     FormData* httpBody = d->m_firstRequest.httpBody();
-    if (httpBody && !httpBody->isEmpty()) {
-        size_t numElements = httpBody->elements().size();
-
-        // handle the most common case (i.e. no file upload)
-        if (numElements < 2) {
-            Vector<char> body;
-            httpBody->flatten(body);
-            soup_message_set_request(soupMessage, d->m_firstRequest.httpContentType().utf8().data(),
-                                     SOUP_MEMORY_COPY, body.data(), body.size());
-        } else {
-            /*
-             * we have more than one element to upload, and some may
-             * be (big) files, which we will want to mmap instead of
-             * copying into memory; TODO: support upload of non-local
-             * (think sftp://) files by using GIO?
-             */
-            soup_message_body_set_accumulate(soupMessage->request_body, FALSE);
-            for (size_t i = 0; i < numElements; i++) {
-                const FormDataElement& element = httpBody->elements()[i];
-
-                if (element.m_type == FormDataElement::data)
-                    soup_message_body_append(soupMessage->request_body, SOUP_MEMORY_TEMPORARY, element.m_data.data(), element.m_data.size());
-                else {
-                    /*
-                     * mapping for uploaded files code inspired by technique used in
-                     * libsoup's simple-httpd test
-                     */
-                    GOwnPtr<GError> error;
-                    CString fileName = fileSystemRepresentation(element.m_filename);
-                    GMappedFile* fileMapping = g_mapped_file_new(fileName.data(), false, &error.outPtr());
-
-                    if (error) {
-                        g_signal_handlers_disconnect_matched(soupMessage, G_SIGNAL_MATCH_DATA,
-                                                             0, 0, 0, 0, handle);
-                        d->m_soupMessage.clear();
-
-                        return false;
-                    }
-
-                    SoupBuffer* soupBuffer = soup_buffer_new_with_owner(g_mapped_file_get_contents(fileMapping),
-                                                                        g_mapped_file_get_length(fileMapping),
-                                                                        fileMapping,
-                                                                        reinterpret_cast<GDestroyNotify>(g_mapped_file_unref));
-                    soup_message_body_append_buffer(soupMessage->request_body, soupBuffer);
-                    soup_buffer_free(soupBuffer);
-                }
-            }
-        }
+    CString contentType = d->m_firstRequest.httpContentType().utf8().data();
+    if (httpBody && !httpBody->isEmpty()
+        && !addFormElementsToSoupMessage(soupMessage, contentType.data(), httpBody, d->m_bodySize)) {
+        // We failed to prepare the body data, so just fail this load.
+        g_signal_handlers_disconnect_matched(soupMessage, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, handle);
+        d->m_soupMessage.clear();
+        return false;
     }
 
     // balanced by a deref() in cleanupSoupRequestOperation, which should always run