Reviewed by John.
authordarin <darin@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 14 Feb 2005 20:06:55 +0000 (20:06 +0000)
committerdarin <darin@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 14 Feb 2005 20:06:55 +0000 (20:06 +0000)
        - fixed <rdar://problem/3686434> Safari uses too much RAM on file upload, leading to malloc errors and crashes (HP printers)

        * WebView.subproj/WebFormDataStream.h: Added webSetHTTPBody, which creates and connects an appropriate
        stream to an NSMutableURLRequest.
        * WebView.subproj/WebFormDataStream.m: Added implementation here.

        * WebCoreSupport.subproj/WebBridge.m:
        (-[WebBridge syncLoadResourceWithURL:customHeaders:postData:finalURL:responseHeaders:statusCode:]):
        Use webSetHTTPBody.
        * WebCoreSupport.subproj/WebSubresourceClient.m:
        (+[WebSubresourceClient startLoadingResource:withURL:customHeaders:postData:referrer:forDataSource:]):
        Use webSetHTTPBody.
        * WebView.subproj/WebFrame.m:
        (-[WebFrame _loadItem:withLoadType:]): Use webSetHTTPBody.
        (-[WebFrame _postWithURL:referrer:target:data:contentType:triggeringEvent:form:formValues:]): Ditto.

        * English.lproj/StringsNotToBeLocalized.txt: Updated for this change and other recent changes.

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

WebKit/ChangeLog
WebKit/English.lproj/StringsNotToBeLocalized.txt
WebKit/WebCoreSupport.subproj/WebBridge.m
WebKit/WebCoreSupport.subproj/WebSubresourceClient.m
WebKit/WebCoreSupport.subproj/WebSubresourceLoader.m
WebKit/WebView.subproj/WebFormDataStream.h
WebKit/WebView.subproj/WebFormDataStream.m
WebKit/WebView.subproj/WebFrame.m

index 988406317ace65204523a13d2cf9d60af3feccad..16ec4bdf1d065ba7a043b8f698642bae266d2b58 100644 (file)
@@ -1,3 +1,25 @@
+2005-02-14  Darin Adler  <darin@apple.com>
+
+        Reviewed by John.
+
+        - fixed <rdar://problem/3686434> Safari uses too much RAM on file upload, leading to malloc errors and crashes (HP printers)
+
+        * WebView.subproj/WebFormDataStream.h: Added webSetHTTPBody, which creates and connects an appropriate
+        stream to an NSMutableURLRequest.
+        * WebView.subproj/WebFormDataStream.m: Added implementation here.
+
+        * WebCoreSupport.subproj/WebBridge.m:
+        (-[WebBridge syncLoadResourceWithURL:customHeaders:postData:finalURL:responseHeaders:statusCode:]):
+        Use webSetHTTPBody.
+        * WebCoreSupport.subproj/WebSubresourceClient.m:
+        (+[WebSubresourceClient startLoadingResource:withURL:customHeaders:postData:referrer:forDataSource:]):
+        Use webSetHTTPBody.
+        * WebView.subproj/WebFrame.m:
+        (-[WebFrame _loadItem:withLoadType:]): Use webSetHTTPBody.
+        (-[WebFrame _postWithURL:referrer:target:data:contentType:triggeringEvent:form:formValues:]): Ditto.
+
+        * English.lproj/StringsNotToBeLocalized.txt: Updated for this change and other recent changes.
+
 2005-02-11  Richard Williamson   <rjw@apple.com>
 
        Fixed <rdar://problem/4002505> 8A378: Endlessly animating gif's on http://www.entropy.ch
index 41ef47599a09ab5332803a95f8710e3d4338699d..10a5fe51cd2189441480b316c79b3f6212184c4a 100644 (file)
@@ -21,6 +21,7 @@
 "%@://%@"
 "%d"
 "%dpx"
+"%lld"
 "([\"\'#$/-`{"
 ")].,;:?\'!\"%*-/}"
 ","
 "application/x-webarchive"
 "application/xhtml+xml"
 "application/xml"
+"basefont"
 "c"
 "canGoBack"
 "canGoForward"
 "destination-in"
 "destination-out"
 "destination-over"
+"dir"
 "displayTitle"
+"doctype"
 "estimatedProgress"
 "ftp:"
 "htm"
 "image/tiff"
 "img"
 "isLoading"
+"isindex"
 "javascript:"
 "lastVisitedDate"
 "localhost"
 "public.tiff"
 "rgb(%.0f,%.0f,%.0f)"
 "rgba(%.0f,%.0f,%.0f,%f)"
+"s"
 "source-atop"
 "source-in"
 "source-out"
 "text/xml"
 "text/xsl"
 "tiff"
+"u"
 "unable to get glyphsfor %@ %f error = (%d)"
 "unexpected result from ATSUGetGlyphBounds():  actualNumBounds(%d) != 1"
 "url_icon"
 "webplugin"
 "x"
 "x-apple-web-kit/"
+"xml"
 "~/Library/Icons"
 DOM.subproj/WebDOMOperations.m:"background"
 DOM.subproj/WebDOMOperations.m:"data"
@@ -431,20 +439,27 @@ WebView.subproj/WebDefaultContextMenuDelegate.m:"Search in Spotlight"
 WebView.subproj/WebHTMLView.m:"%0.fpx"
 WebView.subproj/WebHTMLView.m:"Search With Google"
 WebView.subproj/WebHTMLView.m:"Version:"
+WebView.subproj/WebHTMLView.m:"applet"
 WebView.subproj/WebHTMLView.m:"b"
 WebView.subproj/WebHTMLView.m:"baseline"
 WebView.subproj/WebHTMLView.m:"black"
+WebView.subproj/WebHTMLView.m:"body"
 WebView.subproj/WebHTMLView.m:"bold"
 WebView.subproj/WebHTMLView.m:"center"
 WebView.subproj/WebHTMLView.m:"color"
+WebView.subproj/WebHTMLView.m:"font"
+WebView.subproj/WebHTMLView.m:"head"
 WebView.subproj/WebHTMLView.m:"i"
 WebView.subproj/WebHTMLView.m:"italic"
 WebView.subproj/WebHTMLView.m:"justify"
 WebView.subproj/WebHTMLView.m:"left"
 WebView.subproj/WebHTMLView.m:"line-through"
+WebView.subproj/WebHTMLView.m:"menu"
 WebView.subproj/WebHTMLView.m:"none"
 WebView.subproj/WebHTMLView.m:"normal"
+WebView.subproj/WebHTMLView.m:"object"
 WebView.subproj/WebHTMLView.m:"right"
+WebView.subproj/WebHTMLView.m:"strike"
 WebView.subproj/WebHTMLView.m:"style"
 WebView.subproj/WebHTMLView.m:"sub"
 WebView.subproj/WebHTMLView.m:"super"
index 9135db1cd4aeba88bd366c1c9052729062d68b83..7e02b8d18057d99d1c998c968c692e533fc134d7 100644 (file)
@@ -14,6 +14,7 @@
 #import <WebKit/WebDefaultUIDelegate.h>
 #import <WebKit/WebEditingDelegate.h>
 #import <WebKit/WebFileButton.h>
+#import <WebKit/WebFormDataStream.h>
 #import <WebKit/WebFormDelegate.h>
 #import <WebKit/WebFrameInternal.h>
 #import <WebKit/WebFrameLoadDelegate.h>
@@ -465,11 +466,7 @@ NSString *WebPluginContainerKey =   @"WebPluginContainer";
 
     if (postData) {
         [newRequest setHTTPMethod:@"POST"];
-        
-        // FIXME: This will have to be expanded to handle filenames and arrays with more than one element to fix file uploading.
-        if ([postData count] == 1 && [[postData objectAtIndex:0] isKindOfClass:[NSData class]]) {
-            [newRequest setHTTPBody:(NSData *)[postData objectAtIndex:0]];
-        }
+        webSetHTTPBody(newRequest, postData);
     }
 
     NSEnumerator *e = [requestHeaders keyEnumerator];
index d78e79cb7abbcc6c7b093e344a29a25c49bbba35..7101a1942c2a0cdeae9fef83cf56eb8bd78244af 100644 (file)
@@ -8,6 +8,7 @@
 #import <WebKit/WebAssertions.h>
 #import <WebKit/WebBridge.h>
 #import <WebKit/WebDataSourcePrivate.h>
+#import <WebKit/WebFormDataStream.h>
 #import <WebKit/WebFrame.h>
 #import <WebKit/WebKitErrorsPrivate.h>
 #import <WebKit/WebViewPrivate.h>
     NSMutableURLRequest *newRequest = [[NSMutableURLRequest alloc] initWithURL:URL];
 
     [newRequest setHTTPMethod:@"POST"];
-
-    // FIXME: This will have to be expanded to handle filenames and arrays with more than one element to fix file uploading.
-    if ([postData count] == 1 && [[postData objectAtIndex:0] isKindOfClass:[NSData class]]) {
-        [newRequest setHTTPBody:(NSData *)[postData objectAtIndex:0]];
-    }
+    webSetHTTPBody(newRequest, postData);
 
     WebSubresourceClient *client = [self startLoadingResource:rLoader withRequest:newRequest customHeaders:customHeaders referrer:referrer forDataSource:source];
     [newRequest release];
index d78e79cb7abbcc6c7b093e344a29a25c49bbba35..7101a1942c2a0cdeae9fef83cf56eb8bd78244af 100644 (file)
@@ -8,6 +8,7 @@
 #import <WebKit/WebAssertions.h>
 #import <WebKit/WebBridge.h>
 #import <WebKit/WebDataSourcePrivate.h>
+#import <WebKit/WebFormDataStream.h>
 #import <WebKit/WebFrame.h>
 #import <WebKit/WebKitErrorsPrivate.h>
 #import <WebKit/WebViewPrivate.h>
     NSMutableURLRequest *newRequest = [[NSMutableURLRequest alloc] initWithURL:URL];
 
     [newRequest setHTTPMethod:@"POST"];
-
-    // FIXME: This will have to be expanded to handle filenames and arrays with more than one element to fix file uploading.
-    if ([postData count] == 1 && [[postData objectAtIndex:0] isKindOfClass:[NSData class]]) {
-        [newRequest setHTTPBody:(NSData *)[postData objectAtIndex:0]];
-    }
+    webSetHTTPBody(newRequest, postData);
 
     WebSubresourceClient *client = [self startLoadingResource:rLoader withRequest:newRequest customHeaders:customHeaders referrer:referrer forDataSource:source];
     [newRequest release];
index de4871c50f1bc2da2dfa5f83270822701a92fd47..31edda1408b078f26f9cf3f0a13812d013a8e454 100644 (file)
@@ -1,13 +1,9 @@
-/* Copyright 2004, Apple Computer, Inc. */
+/*
+    WebFormDataStream.h
+    Copyright 2005 Apple Computer, Inc.
+*/
 
-#import <Foundation/Foundation.h>
+@class NSArray;
+@class NSMutableURLRequest;
 
-@interface WebFormDataStream : NSInputStream
-{
-    NSArray *_formDataArray;
-}
-
-- (id)initWithFormDataArray:(NSArray *)array;
-- (NSArray *)formDataArray;
-
-@end
+void webSetHTTPBody(NSMutableURLRequest *request, NSArray *formData);
index c154effd218fbbf8493242f48059f5da2af50b97..acecea3f27a2fc3078fcbc38228a6fdf0fc26d80 100644 (file)
-/* Copyright 2004, Apple Computer, Inc. */
+/*
+    WebFormDataStream.m
+    originally written by Becky Willrich, additional code by Darin Adler
+    Copyright 2005 Apple Computer, Inc.
+*/
 
 #import "WebFormDataStream.h"
 
-@implementation WebFormDataStream
+#import <sys/types.h>
+#import <sys/stat.h>
 
-- (id)initWithFormDataArray:(NSArray *)array
+#import <CoreFoundation/CFStreamAbstract.h>
+
+#import "WebAssertions.h"
+#import "WebNSObjectExtras.h"
+
+static void formEventCallback(CFReadStreamRef stream, CFStreamEventType type, void *context);
+
+typedef struct {
+    CFMutableSetRef scheduledRunLoopPairs;
+    CFMutableArrayRef formDataArray;
+    CFReadStreamRef currentStream;
+    CFReadStreamRef formStream;
+} FormStreamFields;
+
+typedef struct {
+    CFRunLoopRef runLoop;
+    CFStringRef mode;
+} SchedulePair;
+
+static const void *pairRetain(CFAllocatorRef alloc, const void *value)
+{
+    const SchedulePair *pair = (const SchedulePair *)value;
+
+    SchedulePair *result = CFAllocatorAllocate(alloc, sizeof(SchedulePair), 0);
+    CFRetain(pair->runLoop);
+    result->runLoop = pair->runLoop;
+    result->mode = CFStringCreateCopy(alloc, pair->mode);
+    return result;
+}
+
+static void pairRelease(CFAllocatorRef alloc, const void *value)
+{
+    const SchedulePair *pair = (const SchedulePair *)value;
+
+    CFRelease(pair->runLoop);
+    CFRelease(pair->mode);
+    CFAllocatorDeallocate(alloc, (void *)pair);
+}
+
+static Boolean pairEqual(const void *a, const void *b)
+{
+    const SchedulePair *pairA = (const SchedulePair *)a;
+    const SchedulePair *pairB = (const SchedulePair *)b;
+
+    return pairA->runLoop == pairB->runLoop && CFEqual(pairA->mode, pairB->mode);
+}
+
+static CFHashCode pairHash(const void *value)
+{
+    const SchedulePair *pair = (const SchedulePair *)value;
+
+    return ((CFHashCode)pair->runLoop) ^ CFHash(pair->mode);
+}
+
+static void closeCurrentStream(FormStreamFields *form)
+{
+    if (form->currentStream) {
+        CFReadStreamClose(form->currentStream);
+        CFReadStreamSetClient(form->currentStream, kCFStreamEventNone, NULL, NULL);
+        CFRelease(form->currentStream);
+        form->currentStream = NULL;
+    }
+}
+
+static void scheduleWithPair(const void *value, void *context)
+{
+    const SchedulePair *pair = (const SchedulePair *)value;
+    CFReadStreamRef stream = (CFReadStreamRef)context;
+
+    CFReadStreamScheduleWithRunLoop(stream, pair->runLoop, pair->mode);
+}
+
+static void advanceCurrentStream(FormStreamFields *form)
+{
+    closeCurrentStream(form);
+
+    // Handle the case where we're at the end of the array.
+    if (CFArrayGetCount(form->formDataArray) == 0) {
+        return;
+    }
+
+    // Create the new stream.
+    CFAllocatorRef alloc = CFGetAllocator(form->formDataArray);
+    CFTypeRef nextInput = CFArrayGetValueAtIndex(form->formDataArray, 0);
+    if (CFGetTypeID(nextInput) == CFDataGetTypeID()) {
+        // nextInput is a CFData containing an absolute path
+        CFDataRef data = (CFDataRef)nextInput;
+        form->currentStream = CFReadStreamCreateWithBytesNoCopy(alloc, CFDataGetBytePtr(data), CFDataGetLength(data), kCFAllocatorNull);
+    } else {
+        // nextInput is a CFString containing an absolute path
+        CFStringRef path = (CFStringRef)nextInput;
+        CFURLRef fileURL = CFURLCreateWithFileSystemPath(alloc, path, kCFURLPOSIXPathStyle, FALSE);
+        form->currentStream = CFReadStreamCreateWithFile(alloc, fileURL);
+        CFRelease(fileURL);
+    }
+    CFArrayRemoveValueAtIndex(form->formDataArray, 0);
+
+    // Set up the callback.
+    CFStreamClientContext context = { 0, form, NULL, NULL, NULL };
+    CFReadStreamSetClient(form->currentStream, kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered,
+        formEventCallback, &context);
+
+    // Schedule with the current set of run loops.
+    CFSetApplyFunction(form->scheduledRunLoopPairs, scheduleWithPair, form->currentStream);
+}
+
+static void openNextStream(FormStreamFields *form)
+{
+    // Skip over any streams we can't open.
+    // For some purposes we might want to return an error, but the current NSURLConnection
+    // can't really do anything useful with an error at this point, so this is better.
+    advanceCurrentStream(form);
+    while (form->currentStream && !CFReadStreamOpen(form->currentStream)) {
+        advanceCurrentStream(form);
+    }
+}
+
+static void *formCreate(CFReadStreamRef stream, void *context)
+{
+    CFArrayRef formDataArray = (CFArrayRef)context;
+
+    CFSetCallBacks runLoopAndModeCallBacks = { 0, pairRetain, pairRelease, NULL, pairEqual, pairHash };
+
+    CFAllocatorRef alloc = CFGetAllocator(stream);
+    FormStreamFields *newInfo = CFAllocatorAllocate(alloc, sizeof(FormStreamFields), 0);
+    newInfo->scheduledRunLoopPairs = CFSetCreateMutable(alloc, 0, &runLoopAndModeCallBacks);
+    newInfo->formDataArray = CFArrayCreateMutableCopy(alloc, CFArrayGetCount(formDataArray), formDataArray);
+    newInfo->currentStream = NULL;
+    newInfo->formStream = stream; // Don't retain. That would create a reference cycle.
+    return newInfo;
+}
+
+static void formFinalize(CFReadStreamRef stream, void *context)
 {
-    [super init];
-    _formDataArray = [array copy];
-    return self;
+    FormStreamFields *form = (FormStreamFields *)context;
+
+    closeCurrentStream(context);
+    CFRelease(form->scheduledRunLoopPairs);
+    CFRelease(form->formDataArray);
+    CFAllocatorDeallocate(CFGetAllocator(stream), context);
 }
 
-- (void)dealloc
+static Boolean formOpen(CFReadStreamRef stream, CFStreamError *error, Boolean *openComplete, void *context)
 {
-    [_formDataArray release];
-    [super dealloc];
+    FormStreamFields *form = (FormStreamFields *)context;
+
+    openNextStream(form);
+
+    *openComplete = TRUE;
+    error->error = 0;
+    return TRUE;
 }
 
-- (NSArray *)formDataArray
+static CFIndex formRead(CFReadStreamRef stream, UInt8 *buffer, CFIndex bufferLength, CFStreamError *error, Boolean *atEOF, void *context)
 {
-    return _formDataArray;
+    FormStreamFields *form = (FormStreamFields *)context;
+
+    while (form->currentStream) {
+        CFIndex bytesRead = CFReadStreamRead(form->currentStream, buffer, bufferLength);
+        if (bytesRead < 0) {
+            *error = CFReadStreamGetError(form->currentStream);
+            return -1;
+        }
+        if (bytesRead > 0) {
+            error->error = 0;
+            *atEOF = FALSE;
+            return bytesRead;
+        }
+        openNextStream(form);
+    }
+
+    error->error = 0;
+    *atEOF = TRUE;
+    return 0;
+}
+
+static Boolean formCanRead(CFReadStreamRef stream, void *context)
+{
+    FormStreamFields *form = (FormStreamFields *)context;
+
+    while (form->currentStream && CFReadStreamGetStatus(form->currentStream) == kCFStreamStatusAtEnd) {
+        openNextStream(form);
+    }
+    if (!form->currentStream) {
+        CFReadStreamSignalEvent(stream, kCFStreamEventEndEncountered, NULL);
+        return FALSE;
+    }
+    return CFReadStreamHasBytesAvailable(form->currentStream);
+}
+
+static void formClose(CFReadStreamRef stream, void *context)
+{
+    FormStreamFields *form = (FormStreamFields *)context;
+
+    closeCurrentStream(form);
 }
 
-@end
+static void formSchedule(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *context)
+{
+    FormStreamFields *form = (FormStreamFields *)context;
+
+    if (form->currentStream) {
+        CFReadStreamScheduleWithRunLoop(form->currentStream, runLoop, runLoopMode);
+    }
+    SchedulePair pair = { runLoop, runLoopMode };
+    CFSetAddValue(form->scheduledRunLoopPairs, &pair);
+}
+
+static void formUnschedule(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *context)
+{
+    FormStreamFields *form = (FormStreamFields *)context;
+
+    if (form->currentStream) {
+        CFReadStreamUnscheduleFromRunLoop(form->currentStream, runLoop, runLoopMode);
+    }
+    SchedulePair pair = { runLoop, runLoopMode };
+    CFSetRemoveValue(form->scheduledRunLoopPairs, &pair);
+}
+
+static void formEventCallback(CFReadStreamRef stream, CFStreamEventType type, void *context)
+{
+    FormStreamFields *form = (FormStreamFields *)context;
+
+    switch (type) {
+    case kCFStreamEventHasBytesAvailable:
+        CFReadStreamSignalEvent(form->formStream, kCFStreamEventHasBytesAvailable, NULL);
+        break;
+    case kCFStreamEventErrorOccurred: {
+        CFStreamError readStreamError = CFReadStreamGetError(stream);
+        CFReadStreamSignalEvent(form->formStream, kCFStreamEventErrorOccurred, &readStreamError);
+        break;
+    }
+    case kCFStreamEventEndEncountered:
+        openNextStream(form);
+        if (!form->currentStream) {
+            CFReadStreamSignalEvent(form->formStream, kCFStreamEventEndEncountered, NULL);
+        }
+        break;
+    case kCFStreamEventNone:
+        ERROR("unexpected kCFStreamEventNone");
+        break;
+    case kCFStreamEventOpenCompleted:
+        ERROR("unexpected kCFStreamEventOpenCompleted");
+        break;
+    case kCFStreamEventCanAcceptBytes:
+        ERROR("unexpected kCFStreamEventCanAcceptBytes");
+        break;
+    }
+}
+
+void webSetHTTPBody(NSMutableURLRequest *request, NSArray *formData)
+{
+    unsigned count = [formData count];
+
+    // Handle the common special case of one piece of form data, with no files.
+    if (count == 1) {
+        id d = [formData objectAtIndex:0];
+        if ([d isKindOfClass:[NSData class]]) {
+            [request setHTTPBody:(NSData *)d];
+            return;
+        }
+    }
+
+    // Precompute the content length so NSURLConnection doesn't use chunked mode.
+    long long length = 0;
+    unsigned i;
+    for (i = 0; i < count; ++i) {
+        id data = [formData objectAtIndex:i];
+        if ([data isKindOfClass:[NSData class]]) {
+            NSData *d = data;
+            length += [d length];
+        } else if ([data isKindOfClass:[NSString class]]) {
+            NSString *s = data;
+            struct stat sb;
+            int statResult = stat([s fileSystemRepresentation], &sb);
+            if (statResult == 0 && (sb.st_mode & S_IFMT) == S_IFREG) {
+                length += sb.st_size;
+            }
+        } else {
+            ERROR("item in form data array is neither NSData nor NSString");
+            return;
+        }
+    }
+
+    // Set the length.
+    [request setValue:[NSString stringWithFormat:@"%lld", length] forHTTPHeaderField:@"Content-Length"];
+
+    // Create and set the stream.
+    CFReadStreamCallBacks callBacks = { 1, formCreate, formFinalize, NULL,
+        formOpen, NULL, formRead, NULL, formCanRead, formClose,
+        NULL, NULL, NULL, formSchedule, formUnschedule
+    };
+    CFReadStreamRef stream = CFReadStreamCreate(NULL, &callBacks, formData);
+    [request setHTTPBodyStream:(NSInputStream *)stream];
+    CFRelease(stream);
+}
index d7c59cf5360d11d7b0b15a075238654a47ba8638..06bc03bbf90242bcdcd2e48fa14a231a35a3d82e 100644 (file)
@@ -14,6 +14,7 @@
 #import <WebKit/WebDefaultResourceLoadDelegate.h>
 #import <WebKit/WebDefaultUIDelegate.h>
 #import <WebKit/WebDocumentInternal.h>
+#import <WebKit/WebFormDataStream.h>
 #import <WebKit/WebFrameLoadDelegate.h>
 #import <WebKit/WebFrameViewInternal.h>
 #import <WebKit/WebHistoryPrivate.h>
@@ -1376,12 +1377,8 @@ static CFAbsoluteTime _timeOfLastCompletedLoad;
             if (formData) {
                 [request setHTTPMethod:@"POST"];
                 [request setHTTPReferrer:[item formReferrer]];
-
-                // FIXME: This will have to be expanded to handle filenames and arrays with more than one element to fix file uploading.
-                if ([formData count] == 1 && [[formData objectAtIndex:0] isKindOfClass:[NSData class]]) {
-                    [request setHTTPBody:(NSData *)[formData objectAtIndex:0]];
-                    [request setHTTPContentType:[item formContentType]];
-                }
+                webSetHTTPBody(request, formData);
+                [request setHTTPContentType:[item formContentType]];
 
                 // Slight hack to test if the WF cache contains the page we're going to.  We want
                 // to know this before talking to the policy delegate, since it affects whether we
@@ -1995,12 +1992,8 @@ static CFAbsoluteTime _timeOfLastCompletedLoad;
     [self _addExtraFieldsToRequest:request alwaysFromRequest:YES];
     [request setHTTPReferrer:referrer];
     [request setHTTPMethod:@"POST"];
-
-    // FIXME: This will have to be expanded to handle filenames and arrays with more than one element to fix file uploading.
-    if ([postData count] == 1 && [[postData objectAtIndex:0] isKindOfClass:[NSData class]]) {
-        [request setHTTPBody:(NSData *)[postData objectAtIndex:0]];
-        [request setHTTPContentType:contentType];
-    }
+    webSetHTTPBody(request, postData);
+    [request setHTTPContentType:contentType];
 
     NSDictionary *action = [self _actionInformationForLoadType:WebFrameLoadTypeStandard isFormSubmission:YES event:event originalURL:URL];
     WebFormState *formState = nil;