Changeset 112482 in webkit


Ignore:
Timestamp:
Mar 28, 2012 5:58:42 PM (12 years ago)
Author:
ap@apple.com
Message:

[Win] Some Blob tests crash in CFNetwork in advanceCurrentStream(FormStreamFields*)
https://bugs.webkit.org/show_bug.cgi?id=82386
<rdar://problem/11121501>

Reviewed by Brady Eidson.

Covered by existing tests.

  • platform/network/cf/FormDataStreamCFNet.cpp:
  • platform/network/cf/FormDataStreamCFNet.h:

Moved low level implementation from FormDataStreamMac, and exposed an additional function
to implement Foundation-based API on top of this.

  • platform/network/mac/FormDataStreamMac.h:
  • platform/network/mac/FormDataStreamMac.mm:

Rely on toll-free bridging and implementation details to make this work without duplicating
code.
Note that httpBodyFromStream is confusing - I don't see how it can work when sending serialized
requests across process boundary. We probably only get away with this because we don't attempt
to send requests to UI process once they already have streams associated with them.

  • WebCore.vcproj/WebCore.vcproj: Added missing platform/cf files.
  • platform/cf/FileSystemCF.cpp: Fixed include style.
  • platform/win/FileSystemWin.cpp: (WebCore::fileSystemRepresentation): Ifdef out a broken

implementation.

Location:
trunk/Source/WebCore
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r112481 r112482  
     12012-03-28  Alexey Proskuryakov  <ap@apple.com>
     2
     3        [Win] Some Blob tests crash in CFNetwork in advanceCurrentStream(FormStreamFields*)
     4        https://bugs.webkit.org/show_bug.cgi?id=82386
     5        <rdar://problem/11121501>
     6
     7        Reviewed by Brady Eidson.
     8
     9        Covered by existing tests.
     10
     11        * platform/network/cf/FormDataStreamCFNet.cpp:
     12        * platform/network/cf/FormDataStreamCFNet.h:
     13        Moved low level implementation from FormDataStreamMac, and exposed an additional function
     14        to implement Foundation-based API on top of this.
     15
     16        * platform/network/mac/FormDataStreamMac.h:
     17        * platform/network/mac/FormDataStreamMac.mm:
     18        Rely on toll-free bridging and implementation details to make this work without duplicating
     19        code.
     20        Note that httpBodyFromStream is confusing - I don't see how it can work when sending serialized
     21        requests across process boundary. We probably only get away with this because we don't attempt
     22        to send requests to UI process once they already have streams associated with them.
     23
     24        * WebCore.vcproj/WebCore.vcproj: Added missing platform/cf files.
     25
     26        * platform/cf/FileSystemCF.cpp: Fixed include style.
     27
     28        * platform/win/FileSystemWin.cpp: (WebCore::fileSystemRepresentation): Ifdef out a broken
     29        implementation.
     30
    1312012-03-28  Adrienne Walker  <enne@google.com>
    232
  • trunk/Source/WebCore/WebCore.vcproj/WebCore.vcproj

    r112453 r112482  
    2838628386                                </File>
    2838728387                                <File
     28388                                        RelativePath="..\platform\cf\FileSystemCF.cpp"
     28389                                        >
     28390                                </File>
     28391                                <File
    2838828392                                        RelativePath="..\platform\cf\KURLCFNet.cpp"
    2838928393                                        >
     
    2839128395                                <File
    2839228396                                        RelativePath="..\platform\cf\RetainPtr.h"
     28397                                        >
     28398                                </File>
     28399                                <File
     28400                                        RelativePath="..\platform\cf\SchedulePair.cpp"
     28401                                        >
     28402                                </File>
     28403                                <File
     28404                                        RelativePath="..\platform\cf\SchedulePair.h"
    2839328405                                        >
    2839428406                                </File>
  • trunk/Source/WebCore/platform/cf/FileSystemCF.cpp

    r76614 r112482  
    2727 */
    2828
    29 #import "config.h"
    30 #import "FileSystem.h"
     29#include "config.h"
     30#include "FileSystem.h"
    3131
    32 #import "PlatformString.h"
    33 #import <wtf/RetainPtr.h>
    34 #import <wtf/text/CString.h>
     32#include "PlatformString.h"
     33#include <wtf/RetainPtr.h>
     34#include <wtf/text/CString.h>
    3535
    3636namespace WebCore {
  • trunk/Source/WebCore/platform/network/cf/FormDataStreamCFNet.cpp

    r92530 r112482  
    11/*
    2  * Copyright (C) 2005, 2006, 2007 Apple Inc.  All rights reserved.
     2 * Copyright (C) 2005, 2006, 2007, 2012 Apple Inc.  All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    2727 */
    2828
    29 /* originally written by Becky Willrich, additional code by Darin Adler */
    30 
    3129#include "config.h"
    3230#include "FormDataStreamCFNet.h"
    3331
    34 #if USE(CFNETWORK)
    35 
     32#include "BlobRegistryImpl.h"
    3633#include "FileSystem.h"
    3734#include "FormData.h"
    38 #include <CFNetwork/CFURLRequestPriv.h>
     35#include "SchedulePair.h"
     36#include <sys/stat.h>
    3937#include <sys/types.h>
    4038#include <wtf/Assertions.h>
    4139#include <wtf/HashMap.h>
     40#include <wtf/MainThread.h>
    4241#include <wtf/RetainPtr.h>
    43 #include <wtf/text/CString.h>
     42#include <wtf/StdLibExtras.h>
     43#include <wtf/Threading.h>
     44
     45#if PLATFORM(IOS)
     46#include <MacErrors.h>
     47#else
     48#include <CoreServices/CoreServices.h>
     49#endif
    4450
    4551#if PLATFORM(MAC)
    46 #include "WebCoreSystemInterface.h"
     52extern "C" void CFURLRequestSetHTTPRequestBody(CFMutableURLRequestRef mutableHTTPRequest, CFDataRef httpBody);
     53extern "C" void CFURLRequestSetHTTPHeaderFieldValue(CFMutableURLRequestRef mutableHTTPRequest, CFStringRef httpHeaderField, CFStringRef httpHeaderFieldValue);
     54extern "C" void CFURLRequestSetHTTPRequestBodyStream(CFMutableURLRequestRef req, CFReadStreamRef bodyStream);
     55extern "C" CFReadStreamRef CFURLRequestCopyHTTPRequestBodyStream(CFURLRequestRef request);
    4756#elif PLATFORM(WIN)
    48 #include <WebKitSystemInterface/WebKitSystemInterface.h>
    49 #endif
     57#include <CFNetwork/CFURLRequest.h>
     58#endif
     59
     60typedef struct {
     61    CFIndex version; /* == 1 */
     62    void *(*create)(CFReadStreamRef stream, void *info);
     63    void (*finalize)(CFReadStreamRef stream, void *info);
     64    CFStringRef (*copyDescription)(CFReadStreamRef stream, void *info);
     65    Boolean (*open)(CFReadStreamRef stream, CFStreamError *error, Boolean *openComplete, void *info);
     66    Boolean (*openCompleted)(CFReadStreamRef stream, CFStreamError *error, void *info);
     67    CFIndex (*read)(CFReadStreamRef stream, UInt8 *buffer, CFIndex bufferLength, CFStreamError *error, Boolean *atEOF, void *info);
     68    const UInt8 *(*getBuffer)(CFReadStreamRef stream, CFIndex maxBytesToRead, CFIndex *numBytesRead, CFStreamError *error, Boolean *atEOF, void *info);
     69    Boolean (*canRead)(CFReadStreamRef stream, void *info);
     70    void (*close)(CFReadStreamRef stream, void *info);
     71    CFTypeRef (*copyProperty)(CFReadStreamRef stream, CFStringRef propertyName, void *info);
     72    Boolean (*setProperty)(CFReadStreamRef stream, CFStringRef propertyName, CFTypeRef propertyValue, void *info);
     73    void (*requestEvents)(CFReadStreamRef stream, CFOptionFlags streamEvents, void *info);
     74    void (*schedule)(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info);
     75    void (*unschedule)(CFReadStreamRef stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info);
     76} CFReadStreamCallBacksV1;
     77
     78#if PLATFORM(WIN)
     79#define EXTERN extern "C" __declspec(dllimport)
     80#else
     81#define EXTERN extern "C"
     82#endif
     83
     84EXTERN void CFReadStreamSignalEvent(CFReadStreamRef stream, CFStreamEventType event, const void *error);
     85EXTERN CFReadStreamRef CFReadStreamCreate(CFAllocatorRef alloc, const void *callbacks, void *info);
    5086
    5187namespace WebCore {
    5288
    53 void setHTTPBody(CFMutableURLRequestRef request, PassRefPtr<FormData> formData)
    54 {
    55     if (!formData) {
    56         wkCFURLRequestSetHTTPRequestBodyParts(request, 0);
     89static Mutex& streamFieldsMapMutex()
     90{
     91    DEFINE_STATIC_LOCAL(Mutex, staticMutex, ());
     92    return staticMutex;
     93}
     94
     95static void formEventCallback(CFReadStreamRef stream, CFStreamEventType type, void* context);
     96
     97struct FormContext {
     98    RefPtr<FormData> formData;
     99    unsigned long long streamLength;
     100};
     101
     102struct FormStreamFields {
     103    RefPtr<FormData> formData;
     104    SchedulePairHashSet scheduledRunLoopPairs;
     105    Vector<FormDataElement> remainingElements; // in reverse order
     106    CFReadStreamRef currentStream;
     107#if ENABLE(BLOB)
     108    long long currentStreamRangeLength;
     109#endif
     110    char* currentData;
     111    CFReadStreamRef formStream;
     112    unsigned long long streamLength;
     113    unsigned long long bytesSent;
     114};
     115
     116typedef HashMap<CFReadStreamRef, FormStreamFields*> StreamFieldsMap;
     117static StreamFieldsMap& streamFieldsMap()
     118{
     119    DEFINE_STATIC_LOCAL(StreamFieldsMap, streamFieldsMap, ());
     120    return streamFieldsMap;
     121}
     122
     123static void closeCurrentStream(FormStreamFields *form)
     124{
     125    if (form->currentStream) {
     126        CFReadStreamClose(form->currentStream);
     127        CFReadStreamSetClient(form->currentStream, kCFStreamEventNone, 0, 0);
     128        CFRelease(form->currentStream);
     129        form->currentStream = 0;
     130#if ENABLE(BLOB)
     131        form->currentStreamRangeLength = BlobDataItem::toEndOfFile;
     132#endif
     133    }
     134    if (form->currentData) {
     135        fastFree(form->currentData);
     136        form->currentData = 0;
     137    }
     138}
     139
     140// Return false if we cannot advance the stream. Currently the only possible failure is that the underlying file has been removed or changed since File.slice.
     141static bool advanceCurrentStream(FormStreamFields* form)
     142{
     143    closeCurrentStream(form);
     144
     145    if (form->remainingElements.isEmpty())
     146        return true;
     147
     148    // Create the new stream.
     149    FormDataElement& nextInput = form->remainingElements.last();
     150
     151    if (nextInput.m_type == FormDataElement::data) {
     152        size_t size = nextInput.m_data.size();
     153        char* data = nextInput.m_data.releaseBuffer();
     154        form->currentStream = CFReadStreamCreateWithBytesNoCopy(0, reinterpret_cast<const UInt8*>(data), size, kCFAllocatorNull);
     155        form->currentData = data;
     156    } else {
     157#if ENABLE(BLOB)
     158        // Check if the file has been changed or not if required.
     159        if (nextInput.m_expectedFileModificationTime != BlobDataItem::doNotCheckFileChange) {
     160            time_t fileModificationTime;
     161            if (!getFileModificationTime(nextInput.m_filename, fileModificationTime) || fileModificationTime != static_cast<time_t>(nextInput.m_expectedFileModificationTime))
     162                return false;
     163        }
     164#endif
     165        const String& path = nextInput.m_shouldGenerateFile ? nextInput.m_generatedFilename : nextInput.m_filename;
     166        form->currentStream = CFReadStreamCreateWithFile(0, pathAsURL(path).get());
     167        if (!form->currentStream) {
     168            // The file must have been removed or become unreadable.
     169            return false;
     170        }
     171#if ENABLE(BLOB)
     172        if (nextInput.m_fileStart > 0) {
     173            RetainPtr<CFNumberRef> position(AdoptCF, CFNumberCreate(0, kCFNumberLongLongType, &nextInput.m_fileStart));
     174            CFReadStreamSetProperty(form->currentStream, kCFStreamPropertyFileCurrentOffset, position.get());
     175        }
     176        form->currentStreamRangeLength = nextInput.m_fileLength;
     177#endif
     178    }
     179    form->remainingElements.removeLast();
     180
     181    // Set up the callback.
     182    CFStreamClientContext context = { 0, form, 0, 0, 0 };
     183    CFReadStreamSetClient(form->currentStream, kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered,
     184        formEventCallback, &context);
     185
     186    // Schedule with the current set of run loops.
     187    SchedulePairHashSet::iterator end = form->scheduledRunLoopPairs.end();
     188    for (SchedulePairHashSet::iterator it = form->scheduledRunLoopPairs.begin(); it != end; ++it)
     189        CFReadStreamScheduleWithRunLoop(form->currentStream, (*it)->runLoop(), (*it)->mode());
     190
     191    return true;
     192}
     193
     194static bool openNextStream(FormStreamFields* form)
     195{
     196    // Skip over any streams we can't open.
     197    if (!advanceCurrentStream(form))
     198        return false;
     199    while (form->currentStream && !CFReadStreamOpen(form->currentStream)) {
     200        if (!advanceCurrentStream(form))
     201            return false;
     202    }
     203    return true;
     204}
     205
     206static void* formCreate(CFReadStreamRef stream, void* context)
     207{
     208    FormContext* formContext = static_cast<FormContext*>(context);
     209
     210    FormStreamFields* newInfo = new FormStreamFields;
     211    newInfo->formData = formContext->formData.release();
     212    newInfo->currentStream = 0;
     213#if ENABLE(BLOB)
     214    newInfo->currentStreamRangeLength = BlobDataItem::toEndOfFile;
     215#endif
     216    newInfo->currentData = 0;
     217    newInfo->formStream = stream; // Don't retain. That would create a reference cycle.
     218    newInfo->streamLength = formContext->streamLength;
     219    newInfo->bytesSent = 0;
     220
     221    // Append in reverse order since we remove elements from the end.
     222    size_t size = newInfo->formData->elements().size();
     223    newInfo->remainingElements.reserveInitialCapacity(size);
     224    for (size_t i = 0; i < size; ++i)
     225        newInfo->remainingElements.append(newInfo->formData->elements()[size - i - 1]);
     226
     227    MutexLocker locker(streamFieldsMapMutex());
     228    ASSERT(!streamFieldsMap().contains(stream));
     229    streamFieldsMap().add(stream, newInfo);
     230
     231    return newInfo;
     232}
     233
     234static void formFinishFinalizationOnMainThread(void* context)
     235{
     236    OwnPtr<FormStreamFields> form = adoptPtr(static_cast<FormStreamFields*>(context));
     237
     238    closeCurrentStream(form.get());
     239}
     240
     241static void formFinalize(CFReadStreamRef stream, void* context)
     242{
     243    FormStreamFields* form = static_cast<FormStreamFields*>(context);
     244
     245    MutexLocker locker(streamFieldsMapMutex());
     246
     247    ASSERT(form->formStream == stream);
     248    ASSERT(streamFieldsMap().get(stream) == context);
     249
     250    // Do this right away because the CFReadStreamRef is being deallocated.
     251    // We can't wait to remove this from the map until we finish finalizing
     252    // on the main thread because in theory the freed memory could be reused
     253    // for a new CFReadStream before that runs.
     254    streamFieldsMap().remove(stream);
     255
     256    callOnMainThread(formFinishFinalizationOnMainThread, form);
     257}
     258
     259static Boolean formOpen(CFReadStreamRef, CFStreamError* error, Boolean* openComplete, void* context)
     260{
     261    FormStreamFields* form = static_cast<FormStreamFields*>(context);
     262
     263    bool opened = openNextStream(form);
     264
     265    *openComplete = opened;
     266    error->error = opened ? 0 : fnfErr;
     267    return opened;
     268}
     269
     270static CFIndex formRead(CFReadStreamRef, UInt8* buffer, CFIndex bufferLength, CFStreamError* error, Boolean* atEOF, void* context)
     271{
     272    FormStreamFields* form = static_cast<FormStreamFields*>(context);
     273
     274    while (form->currentStream) {
     275        CFIndex bytesToRead = bufferLength;
     276#if ENABLE(BLOB)
     277        if (form->currentStreamRangeLength != BlobDataItem::toEndOfFile && form->currentStreamRangeLength < bytesToRead)
     278            bytesToRead = static_cast<CFIndex>(form->currentStreamRangeLength);
     279#endif
     280        CFIndex bytesRead = CFReadStreamRead(form->currentStream, buffer, bytesToRead);
     281        if (bytesRead < 0) {
     282            *error = CFReadStreamGetError(form->currentStream);
     283            return -1;
     284        }
     285        if (bytesRead > 0) {
     286            error->error = 0;
     287            *atEOF = FALSE;
     288            form->bytesSent += bytesRead;
     289#if ENABLE(BLOB)
     290            if (form->currentStreamRangeLength != BlobDataItem::toEndOfFile)
     291                form->currentStreamRangeLength -= bytesRead;
     292#endif
     293
     294            return bytesRead;
     295        }
     296        openNextStream(form);
     297    }
     298
     299    error->error = 0;
     300    *atEOF = TRUE;
     301    return 0;
     302}
     303
     304static Boolean formCanRead(CFReadStreamRef stream, void* context)
     305{
     306    FormStreamFields* form = static_cast<FormStreamFields*>(context);
     307
     308    while (form->currentStream && CFReadStreamGetStatus(form->currentStream) == kCFStreamStatusAtEnd)
     309        openNextStream(form);
     310
     311    if (!form->currentStream) {
     312        CFReadStreamSignalEvent(stream, kCFStreamEventEndEncountered, 0);
     313        return FALSE;
     314    }
     315    return CFReadStreamHasBytesAvailable(form->currentStream);
     316}
     317
     318static void formClose(CFReadStreamRef, void* context)
     319{
     320    FormStreamFields* form = static_cast<FormStreamFields*>(context);
     321
     322    closeCurrentStream(form);
     323}
     324
     325static void formSchedule(CFReadStreamRef, CFRunLoopRef runLoop, CFStringRef runLoopMode, void* context)
     326{
     327    FormStreamFields* form = static_cast<FormStreamFields*>(context);
     328
     329    if (form->currentStream)
     330        CFReadStreamScheduleWithRunLoop(form->currentStream, runLoop, runLoopMode);
     331    form->scheduledRunLoopPairs.add(SchedulePair::create(runLoop, runLoopMode));
     332}
     333
     334static void formUnschedule(CFReadStreamRef, CFRunLoopRef runLoop, CFStringRef runLoopMode, void* context)
     335{
     336    FormStreamFields* form = static_cast<FormStreamFields*>(context);
     337
     338    if (form->currentStream)
     339        CFReadStreamUnscheduleFromRunLoop(form->currentStream, runLoop, runLoopMode);
     340    form->scheduledRunLoopPairs.remove(SchedulePair::create(runLoop, runLoopMode));
     341}
     342
     343static void formEventCallback(CFReadStreamRef stream, CFStreamEventType type, void* context)
     344{
     345    FormStreamFields* form = static_cast<FormStreamFields*>(context);
     346
     347    switch (type) {
     348    case kCFStreamEventHasBytesAvailable:
     349        CFReadStreamSignalEvent(form->formStream, kCFStreamEventHasBytesAvailable, 0);
     350        break;
     351    case kCFStreamEventErrorOccurred: {
     352        CFStreamError readStreamError = CFReadStreamGetError(stream);
     353        CFReadStreamSignalEvent(form->formStream, kCFStreamEventErrorOccurred, &readStreamError);
     354        break;
     355    }
     356    case kCFStreamEventEndEncountered:
     357        openNextStream(form);
     358        if (!form->currentStream)
     359            CFReadStreamSignalEvent(form->formStream, kCFStreamEventEndEncountered, 0);
     360        break;
     361    case kCFStreamEventNone:
     362        LOG_ERROR("unexpected kCFStreamEventNone");
     363        break;
     364    case kCFStreamEventOpenCompleted:
     365        LOG_ERROR("unexpected kCFStreamEventOpenCompleted");
     366        break;
     367    case kCFStreamEventCanAcceptBytes:
     368        LOG_ERROR("unexpected kCFStreamEventCanAcceptBytes");
     369        break;
     370    }
     371}
     372
     373void setHTTPBody(CFMutableURLRequestRef request, PassRefPtr<FormData> prpFormData)
     374{
     375    RefPtr<FormData> formData = prpFormData;
     376
     377    if (!formData)
    57378        return;
    58     }
    59 
     379       
    60380    size_t count = formData->elements().size();
    61381
    62     if (count == 0)
    63         return;
    64 
    65382    // Handle the common special case of one piece of form data, with no files.
    66     if (count == 1) {
     383    if (count == 1 && !formData->alwaysStream()) {
    67384        const FormDataElement& element = formData->elements()[0];
    68385        if (element.m_type == FormDataElement::data) {
    69             CFDataRef data = CFDataCreate(0, reinterpret_cast<const UInt8 *>(element.m_data.data()), element.m_data.size());
    70             CFURLRequestSetHTTPRequestBody(request, data);
    71             CFRelease(data);
     386            RetainPtr<CFDataRef> data = adoptCF(CFDataCreate(0, reinterpret_cast<const UInt8 *>(element.m_data.data()), element.m_data.size()));
     387            CFURLRequestSetHTTPRequestBody(request, data.get());
    72388            return;
    73389        }
    74390    }
    75391
    76     RetainPtr<CFMutableArrayRef> array(AdoptCF, CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks));
    77 
     392#if ENABLE(BLOB)
     393    // Check if there is a blob in the form data.
     394    bool hasBlob = false;
    78395    for (size_t i = 0; i < count; ++i) {
    79396        const FormDataElement& element = formData->elements()[i];
    80         if (element.m_type == FormDataElement::data) {
    81             RetainPtr<CFDataRef> data(AdoptCF, CFDataCreate(0, reinterpret_cast<const UInt8*>(element.m_data.data()), element.m_data.size()));
    82             CFArrayAppendValue(array.get(), data.get());
    83         } else {
    84             RetainPtr<CFStringRef> filename(AdoptCF, element.m_filename.createCFString());
    85             CFArrayAppendValue(array.get(), filename.get());
    86         }
    87     }
    88 
    89     wkCFURLRequestSetHTTPRequestBodyParts(request, array.get());
     397        if (element.m_type == FormDataElement::encodedBlob) {
     398            hasBlob = true;
     399            break;
     400        }
     401    }
     402
     403    // If yes, we have to resolve all the blob references and regenerate the form data with only data and file types.
     404    if (hasBlob) {
     405        RefPtr<FormData> newFormData = FormData::create();
     406        newFormData->setAlwaysStream(formData->alwaysStream());
     407        newFormData->setIdentifier(formData->identifier());
     408        for (size_t i = 0; i < count; ++i) {
     409            const FormDataElement& element = formData->elements()[i];
     410            if (element.m_type == FormDataElement::data)
     411                newFormData->appendData(element.m_data.data(), element.m_data.size());
     412            else if (element.m_type == FormDataElement::encodedFile)
     413                newFormData->appendFile(element.m_filename, element.m_shouldGenerateFile);
     414            else {
     415                ASSERT(element.m_type == FormDataElement::encodedBlob);
     416                RefPtr<BlobStorageData> blobData = static_cast<BlobRegistryImpl&>(blobRegistry()).getBlobDataFromURL(KURL(ParsedURLString, element.m_blobURL));
     417                if (blobData) {
     418                    for (size_t j = 0; j < blobData->items().size(); ++j) {
     419                        const BlobDataItem& blobItem = blobData->items()[j];
     420                        if (blobItem.type == BlobDataItem::Data)
     421                            newFormData->appendData(blobItem.data->data() + static_cast<int>(blobItem.offset), static_cast<int>(blobItem.length));
     422                        else {
     423                            ASSERT(blobItem.type == BlobDataItem::File);
     424                            newFormData->appendFileRange(blobItem.path, blobItem.offset, blobItem.length, blobItem.expectedModificationTime);
     425                        }
     426                    }
     427                }
     428            }
     429        }
     430        formData = newFormData.release();
     431        count = formData->elements().size();
     432    }
     433#endif
     434
     435    // Precompute the content length so NSURLConnection doesn't use chunked mode.
     436    long long length = 0;
     437    for (size_t i = 0; i < count; ++i) {
     438        const FormDataElement& element = formData->elements()[i];
     439        if (element.m_type == FormDataElement::data)
     440            length += element.m_data.size();
     441        else {
     442#if ENABLE(BLOB)
     443            // If we're sending the file range, use the existing range length for now. We will detect if the file has been changed right before we read the file and abort the operation if necessary.
     444            if (element.m_fileLength != BlobDataItem::toEndOfFile) {
     445                length += element.m_fileLength;
     446                continue;
     447            }
     448#endif
     449            long long fileSize;
     450            if (getFileSize(element.m_shouldGenerateFile ? element.m_generatedFilename : element.m_filename, fileSize))
     451                length += fileSize;
     452        }
     453    }
     454
     455    // Set the length.
     456    RetainPtr<CFStringRef> lengthString = adoptCF(CFStringCreateWithFormat(0, 0, CFSTR("%lld"), length));
     457    CFURLRequestSetHTTPHeaderFieldValue(request, CFSTR("Content-Length"), lengthString.get());
     458
     459    // Create and set the stream.
     460
     461    // Pass the length along with the formData so it does not have to be recomputed.
     462    FormContext formContext = { formData.release(), length };
     463
     464    CFReadStreamCallBacksV1 callBacks = { 1, formCreate, formFinalize, 0, formOpen, 0, formRead, 0, formCanRead, formClose, 0, 0, 0, formSchedule, formUnschedule
     465    };
     466    RetainPtr<CFReadStreamRef> stream = adoptCF(CFReadStreamCreate(0, static_cast<const void*>(&callBacks), &formContext));
     467
     468    CFURLRequestSetHTTPRequestBodyStream(request, stream.get());
     469}
     470
     471FormData* httpBodyFromStream(CFReadStreamRef stream)
     472{
     473    if (!stream)
     474        return 0;
     475
     476    MutexLocker locker(streamFieldsMapMutex());
     477    FormStreamFields* formStream = streamFieldsMap().get(stream);
     478    if (!formStream)
     479        return 0;
     480    return formStream->formData.get();
    90481}
    91482
    92483PassRefPtr<FormData> httpBodyFromRequest(CFURLRequestRef request)
    93484{
    94     RetainPtr<CFDataRef> bodyData(AdoptCF, CFURLRequestCopyHTTPRequestBody(request));
    95     if (bodyData)
    96         return FormData::create(CFDataGetBytePtr(bodyData.get()), CFDataGetLength(bodyData.get()));
    97 
    98     RetainPtr<CFArrayRef> bodyParts(AdoptCF, wkCFURLRequestCopyHTTPRequestBodyParts(request));
    99     if (bodyParts) {
    100         RefPtr<FormData> formData = FormData::create();
    101 
    102         CFIndex count = CFArrayGetCount(bodyParts.get());
    103         for (CFIndex i = 0; i < count; i++) {
    104             CFTypeRef bodyPart = CFArrayGetValueAtIndex(bodyParts.get(), i);
    105             CFTypeID typeID = CFGetTypeID(bodyPart);
    106             if (typeID == CFStringGetTypeID()) {
    107                 String filename = (CFStringRef)bodyPart;
    108                 formData->appendFile(filename);
    109             } else if (typeID == CFDataGetTypeID()) {
    110                 CFDataRef data = (CFDataRef)bodyPart;
    111                 formData->appendData(CFDataGetBytePtr(data), CFDataGetLength(data));
    112             } else
    113                 ASSERT_NOT_REACHED();
    114         }
    115         return formData.release();
    116     }
    117 
    118     // FIXME: what to do about arbitrary body streams?
    119     return 0;
     485    RetainPtr<CFReadStreamRef> bodyStream = adoptCF(CFURLRequestCopyHTTPRequestBodyStream(request));
     486    return httpBodyFromStream(bodyStream.get());
    120487}
    121488
    122489} // namespace WebCore
    123 
    124 #endif // USE(CFNETWORK)
  • trunk/Source/WebCore/platform/network/cf/FormDataStreamCFNet.h

    r74943 r112482  
    11/*
    2  * Copyright (C) 2005, 2006, 2007 Apple Inc.  All rights reserved.
     2 * Copyright (C) 2005, 2006, 2007, 2012 Apple Inc.  All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    3030#define FormDataStreamCFNet_h
    3131
    32 #if USE(CFNETWORK)
    33 
    34 #include <CoreFoundation/CoreFoundation.h>
    3532#include <wtf/Forward.h>
    3633
     
    3936
    4037namespace WebCore {
    41     class FormData;
    42     void setHTTPBody(CFMutableURLRequestRef, PassRefPtr<FormData>);
    43     PassRefPtr<FormData> httpBodyFromRequest(CFURLRequestRef);
    44 }
    4538
    46 #endif // USE(CFNETWORK)
     39class FormData;
     40
     41void setHTTPBody(CFMutableURLRequestRef, PassRefPtr<FormData>);
     42PassRefPtr<FormData> httpBodyFromRequest(CFURLRequestRef);
     43
     44FormData* httpBodyFromStream(CFReadStreamRef);
     45
     46} // namespace WebCore
    4747
    4848#endif // FormDataStreamCFNet_h
  • trunk/Source/WebCore/platform/network/mac/FormDataStreamMac.h

    r112162 r112482  
    3232#if !USE(CFNETWORK)
    3333
    34 #include "FormData.h"
     34#include <wtf/Forward.h>
    3535
     36@class NSInputStream;
    3637@class NSMutableURLRequest;
    3738
  • trunk/Source/WebCore/platform/network/mac/FormDataStreamMac.mm

    r112302 r112482  
    3232#if !USE(CFNETWORK)
    3333
    34 #import "BlobRegistryImpl.h"
    35 #import "FileSystem.h"
    3634#import "FormData.h"
    37 #import "SchedulePair.h"
    38 #import "WebCoreSystemInterface.h"
    39 #import <sys/stat.h>
    40 #import <sys/types.h>
    41 #import <wtf/Assertions.h>
    42 #import <wtf/MainThread.h>
    43 #import <wtf/StdLibExtras.h>
    44 #import <wtf/Threading.h>
     35#import "FormDataStreamCFNet.h"
     36#import <wtf/PassRefPtr.h>
    4537
    46 #if PLATFORM(IOS)
    47 #import <MacErrors.h>
    48 #else
    49 #import <CoreServices/CoreServices.h>
    50 #endif
     38@interface NSURLRequest (WebNSURLRequestDetails)
     39- (CFURLRequestRef)_CFURLRequest;
     40@end
    5141
    5242namespace WebCore {
    5343
    54 static Mutex& streamFieldsMapMutex()
     44void setHTTPBody(NSMutableURLRequest *request, PassRefPtr<FormData> prpFormData)
    5545{
    56     DEFINE_STATIC_LOCAL(Mutex, staticMutex, ());
    57     return staticMutex;
     46    setHTTPBody(const_cast<CFMutableURLRequestRef>([request _CFURLRequest]), prpFormData);
    5847}
    5948
    60 static void formEventCallback(CFReadStreamRef stream, CFStreamEventType type, void* context);
    61 
    62 struct FormContext {
    63     RefPtr<FormData> formData;
    64     unsigned long long streamLength;
    65 };
    66 
    67 struct FormStreamFields {
    68     RefPtr<FormData> formData;
    69     SchedulePairHashSet scheduledRunLoopPairs;
    70     Vector<FormDataElement> remainingElements; // in reverse order
    71     CFReadStreamRef currentStream;
    72 #if ENABLE(BLOB)
    73     long long currentStreamRangeLength;
    74 #endif
    75     char* currentData;
    76     CFReadStreamRef formStream;
    77     unsigned long long streamLength;
    78     unsigned long long bytesSent;
    79 };
    80 
    81 typedef HashMap<CFReadStreamRef, FormStreamFields*> StreamFieldsMap;
    82 static StreamFieldsMap& streamFieldsMap()
     49FormData* httpBodyFromStream(NSInputStream *stream)
    8350{
    84     DEFINE_STATIC_LOCAL(StreamFieldsMap, streamFieldsMap, ());
    85     return streamFieldsMap;
    86 }
    87 
    88 static void closeCurrentStream(FormStreamFields *form)
    89 {
    90     if (form->currentStream) {
    91         CFReadStreamClose(form->currentStream);
    92         CFReadStreamSetClient(form->currentStream, kCFStreamEventNone, NULL, NULL);
    93         CFRelease(form->currentStream);
    94         form->currentStream = NULL;
    95 #if ENABLE(BLOB)
    96         form->currentStreamRangeLength = BlobDataItem::toEndOfFile;
    97 #endif
    98     }
    99     if (form->currentData) {
    100         fastFree(form->currentData);
    101         form->currentData = 0;
    102     }
    103 }
    104 
    105 // Return false if we cannot advance the stream. Currently the only possible failure is that the underlying file has been removed or changed since File.slice.
    106 static bool advanceCurrentStream(FormStreamFields* form)
    107 {
    108     closeCurrentStream(form);
    109 
    110     if (form->remainingElements.isEmpty())
    111         return true;
    112 
    113     // Create the new stream.
    114     FormDataElement& nextInput = form->remainingElements.last();
    115 
    116     if (nextInput.m_type == FormDataElement::data) {
    117         size_t size = nextInput.m_data.size();
    118         char* data = nextInput.m_data.releaseBuffer();
    119         form->currentStream = CFReadStreamCreateWithBytesNoCopy(0, reinterpret_cast<const UInt8*>(data), size, kCFAllocatorNull);
    120         form->currentData = data;
    121     } else {
    122 #if ENABLE(BLOB)
    123         // Check if the file has been changed or not if required.
    124         if (nextInput.m_expectedFileModificationTime != BlobDataItem::doNotCheckFileChange) {
    125             time_t fileModificationTime;
    126             if (!getFileModificationTime(nextInput.m_filename, fileModificationTime) || fileModificationTime != static_cast<time_t>(nextInput.m_expectedFileModificationTime))
    127                 return false;
    128         }
    129 #endif
    130         const String& path = nextInput.m_shouldGenerateFile ? nextInput.m_generatedFilename : nextInput.m_filename;
    131         form->currentStream = CFReadStreamCreateWithFile(0, pathAsURL(path).get());
    132         if (!form->currentStream) {
    133             // The file must have been removed or become unreadable.
    134             return false;
    135         }
    136 #if ENABLE(BLOB)
    137         if (nextInput.m_fileStart > 0) {
    138             RetainPtr<CFNumberRef> position(AdoptCF, CFNumberCreate(0, kCFNumberLongLongType, &nextInput.m_fileStart));
    139             CFReadStreamSetProperty(form->currentStream, kCFStreamPropertyFileCurrentOffset, position.get());
    140         }
    141         form->currentStreamRangeLength = nextInput.m_fileLength;
    142 #endif
    143     }
    144     form->remainingElements.removeLast();
    145 
    146     // Set up the callback.
    147     CFStreamClientContext context = { 0, form, NULL, NULL, NULL };
    148     CFReadStreamSetClient(form->currentStream, kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered,
    149         formEventCallback, &context);
    150 
    151     // Schedule with the current set of run loops.
    152     SchedulePairHashSet::iterator end = form->scheduledRunLoopPairs.end();
    153     for (SchedulePairHashSet::iterator it = form->scheduledRunLoopPairs.begin(); it != end; ++it)
    154         CFReadStreamScheduleWithRunLoop(form->currentStream, (*it)->runLoop(), (*it)->mode());
    155 
    156     return true;
    157 }
    158 
    159 static bool openNextStream(FormStreamFields* form)
    160 {
    161     // Skip over any streams we can't open.
    162     if (!advanceCurrentStream(form))
    163         return false;
    164     while (form->currentStream && !CFReadStreamOpen(form->currentStream)) {
    165         if (!advanceCurrentStream(form))
    166             return false;
    167     }
    168     return true;
    169 }
    170 
    171 static void* formCreate(CFReadStreamRef stream, void* context)
    172 {
    173     FormContext* formContext = static_cast<FormContext*>(context);
    174 
    175     FormStreamFields* newInfo = new FormStreamFields;
    176     newInfo->formData = formContext->formData.release();
    177     newInfo->currentStream = NULL;
    178 #if ENABLE(BLOB)
    179     newInfo->currentStreamRangeLength = BlobDataItem::toEndOfFile;
    180 #endif
    181     newInfo->currentData = 0;
    182     newInfo->formStream = stream; // Don't retain. That would create a reference cycle.
    183     newInfo->streamLength = formContext->streamLength;
    184     newInfo->bytesSent = 0;
    185 
    186     // Append in reverse order since we remove elements from the end.
    187     size_t size = newInfo->formData->elements().size();
    188     newInfo->remainingElements.reserveInitialCapacity(size);
    189     for (size_t i = 0; i < size; ++i)
    190         newInfo->remainingElements.append(newInfo->formData->elements()[size - i - 1]);
    191 
    192     MutexLocker locker(streamFieldsMapMutex());
    193     ASSERT(!streamFieldsMap().contains(stream));
    194     streamFieldsMap().add(stream, newInfo);
    195 
    196     return newInfo;
    197 }
    198 
    199 static void formFinishFinalizationOnMainThread(void* context)
    200 {
    201     OwnPtr<FormStreamFields> form = adoptPtr(static_cast<FormStreamFields*>(context));
    202 
    203     closeCurrentStream(form.get());
    204 }
    205 
    206 static void formFinalize(CFReadStreamRef stream, void* context)
    207 {
    208     FormStreamFields* form = static_cast<FormStreamFields*>(context);
    209 
    210     MutexLocker locker(streamFieldsMapMutex());
    211 
    212     ASSERT(form->formStream == stream);
    213     ASSERT(streamFieldsMap().get(stream) == context);
    214 
    215     // Do this right away because the CFReadStreamRef is being deallocated.
    216     // We can't wait to remove this from the map until we finish finalizing
    217     // on the main thread because in theory the freed memory could be reused
    218     // for a new CFReadStream before that runs.
    219     streamFieldsMap().remove(stream);
    220 
    221     callOnMainThread(formFinishFinalizationOnMainThread, form);
    222 }
    223 
    224 static Boolean formOpen(CFReadStreamRef, CFStreamError* error, Boolean* openComplete, void* context)
    225 {
    226     FormStreamFields* form = static_cast<FormStreamFields*>(context);
    227 
    228     bool opened = openNextStream(form);
    229 
    230     *openComplete = opened;
    231     error->error = opened ? 0 : fnfErr;
    232     return opened;
    233 }
    234 
    235 static CFIndex formRead(CFReadStreamRef, UInt8* buffer, CFIndex bufferLength, CFStreamError* error, Boolean* atEOF, void* context)
    236 {
    237     FormStreamFields* form = static_cast<FormStreamFields*>(context);
    238 
    239     while (form->currentStream) {
    240         CFIndex bytesToRead = bufferLength;
    241 #if ENABLE(BLOB)
    242         if (form->currentStreamRangeLength != BlobDataItem::toEndOfFile && form->currentStreamRangeLength < bytesToRead)
    243             bytesToRead = static_cast<CFIndex>(form->currentStreamRangeLength);
    244 #endif
    245         CFIndex bytesRead = CFReadStreamRead(form->currentStream, buffer, bytesToRead);
    246         if (bytesRead < 0) {
    247             *error = CFReadStreamGetError(form->currentStream);
    248             return -1;
    249         }
    250         if (bytesRead > 0) {
    251             error->error = 0;
    252             *atEOF = FALSE;
    253             form->bytesSent += bytesRead;
    254 #if ENABLE(BLOB)
    255             if (form->currentStreamRangeLength != BlobDataItem::toEndOfFile)
    256                 form->currentStreamRangeLength -= bytesRead;
    257 #endif
    258 
    259             return bytesRead;
    260         }
    261         openNextStream(form);
    262     }
    263 
    264     error->error = 0;
    265     *atEOF = TRUE;
    266     return 0;
    267 }
    268 
    269 static Boolean formCanRead(CFReadStreamRef stream, void* context)
    270 {
    271     FormStreamFields* form = static_cast<FormStreamFields*>(context);
    272 
    273     while (form->currentStream && CFReadStreamGetStatus(form->currentStream) == kCFStreamStatusAtEnd) {
    274         openNextStream(form);
    275     }
    276     if (!form->currentStream) {
    277         wkSignalCFReadStreamEnd(stream);
    278         return FALSE;
    279     }
    280     return CFReadStreamHasBytesAvailable(form->currentStream);
    281 }
    282 
    283 static void formClose(CFReadStreamRef, void* context)
    284 {
    285     FormStreamFields* form = static_cast<FormStreamFields*>(context);
    286 
    287     closeCurrentStream(form);
    288 }
    289 
    290 static void formSchedule(CFReadStreamRef, CFRunLoopRef runLoop, CFStringRef runLoopMode, void* context)
    291 {
    292     FormStreamFields* form = static_cast<FormStreamFields*>(context);
    293 
    294     if (form->currentStream)
    295         CFReadStreamScheduleWithRunLoop(form->currentStream, runLoop, runLoopMode);
    296     form->scheduledRunLoopPairs.add(SchedulePair::create(runLoop, runLoopMode));
    297 }
    298 
    299 static void formUnschedule(CFReadStreamRef, CFRunLoopRef runLoop, CFStringRef runLoopMode, void* context)
    300 {
    301     FormStreamFields* form = static_cast<FormStreamFields*>(context);
    302 
    303     if (form->currentStream)
    304         CFReadStreamUnscheduleFromRunLoop(form->currentStream, runLoop, runLoopMode);
    305     form->scheduledRunLoopPairs.remove(SchedulePair::create(runLoop, runLoopMode));
    306 }
    307 
    308 static void formEventCallback(CFReadStreamRef stream, CFStreamEventType type, void* context)
    309 {
    310     FormStreamFields* form = static_cast<FormStreamFields*>(context);
    311 
    312     switch (type) {
    313     case kCFStreamEventHasBytesAvailable:
    314         wkSignalCFReadStreamHasBytes(form->formStream);
    315         break;
    316     case kCFStreamEventErrorOccurred: {
    317         CFStreamError readStreamError = CFReadStreamGetError(stream);
    318         wkSignalCFReadStreamError(form->formStream, &readStreamError);
    319         break;
    320     }
    321     case kCFStreamEventEndEncountered:
    322         openNextStream(form);
    323         if (!form->currentStream) {
    324             wkSignalCFReadStreamEnd(form->formStream);
    325         }
    326         break;
    327     case kCFStreamEventNone:
    328         LOG_ERROR("unexpected kCFStreamEventNone");
    329         break;
    330     case kCFStreamEventOpenCompleted:
    331         LOG_ERROR("unexpected kCFStreamEventOpenCompleted");
    332         break;
    333     case kCFStreamEventCanAcceptBytes:
    334         LOG_ERROR("unexpected kCFStreamEventCanAcceptBytes");
    335         break;
    336     }
    337 }
    338 
    339 void setHTTPBody(NSMutableURLRequest *request, PassRefPtr<FormData> prpFormData)
    340 {
    341     RefPtr<FormData> formData = prpFormData;
    342 
    343     if (!formData)
    344         return;
    345        
    346     size_t count = formData->elements().size();
    347 
    348     // Handle the common special case of one piece of form data, with no files.
    349     if (count == 1 && !formData->alwaysStream()) {
    350         const FormDataElement& element = formData->elements()[0];
    351         if (element.m_type == FormDataElement::data) {
    352             NSData *data = [[NSData alloc] initWithBytes:element.m_data.data() length:element.m_data.size()];
    353             [request setHTTPBody:data];
    354             [data release];
    355             return;
    356         }
    357     }
    358 
    359 #if ENABLE(BLOB)
    360     // Check if there is a blob in the form data.
    361     bool hasBlob = false;
    362     for (size_t i = 0; i < count; ++i) {
    363         const FormDataElement& element = formData->elements()[i];
    364         if (element.m_type == FormDataElement::encodedBlob) {
    365             hasBlob = true;
    366             break;
    367         }
    368     }
    369 
    370     // If yes, we have to resolve all the blob references and regenerate the form data with only data and file types.
    371     if (hasBlob) {
    372         RefPtr<FormData> newFormData = FormData::create();
    373         newFormData->setAlwaysStream(formData->alwaysStream());
    374         newFormData->setIdentifier(formData->identifier());
    375         for (size_t i = 0; i < count; ++i) {
    376             const FormDataElement& element = formData->elements()[i];
    377             if (element.m_type == FormDataElement::data)
    378                 newFormData->appendData(element.m_data.data(), element.m_data.size());
    379             else if (element.m_type == FormDataElement::encodedFile)
    380                 newFormData->appendFile(element.m_filename, element.m_shouldGenerateFile);
    381             else {
    382                 ASSERT(element.m_type == FormDataElement::encodedBlob);
    383                 RefPtr<BlobStorageData> blobData = static_cast<BlobRegistryImpl&>(blobRegistry()).getBlobDataFromURL(KURL(ParsedURLString, element.m_blobURL));
    384                 if (blobData) {
    385                     for (size_t j = 0; j < blobData->items().size(); ++j) {
    386                         const BlobDataItem& blobItem = blobData->items()[j];
    387                         if (blobItem.type == BlobDataItem::Data) {
    388                             newFormData->appendData(blobItem.data->data() + static_cast<int>(blobItem.offset), static_cast<int>(blobItem.length));
    389                         } else {
    390                             ASSERT(blobItem.type == BlobDataItem::File);
    391                             newFormData->appendFileRange(blobItem.path, blobItem.offset, blobItem.length, blobItem.expectedModificationTime);
    392                         }
    393                     }
    394                 }
    395             }
    396         }
    397         formData = newFormData.release();
    398         count = formData->elements().size();
    399     }
    400 #endif
    401 
    402     // Precompute the content length so NSURLConnection doesn't use chunked mode.
    403     long long length = 0;
    404     for (size_t i = 0; i < count; ++i) {
    405         const FormDataElement& element = formData->elements()[i];
    406         if (element.m_type == FormDataElement::data)
    407             length += element.m_data.size();
    408         else {
    409 #if ENABLE(BLOB)
    410             // If we're sending the file range, use the existing range length for now. We will detect if the file has been changed right before we read the file and abort the operation if necessary.
    411             if (element.m_fileLength != BlobDataItem::toEndOfFile) {
    412                 length += element.m_fileLength;
    413                 continue;
    414             }
    415 #endif
    416             long long fileSize;
    417             if (getFileSize(element.m_shouldGenerateFile ? element.m_generatedFilename : element.m_filename, fileSize))
    418                 length += fileSize;
    419         }
    420     }
    421 
    422     // Set the length.
    423     [request setValue:[NSString stringWithFormat:@"%lld", length] forHTTPHeaderField:@"Content-Length"];
    424 
    425     // Create and set the stream.
    426 
    427     // Pass the length along with the formData so it does not have to be recomputed.
    428     FormContext formContext = { formData.release(), length };
    429 
    430     RetainPtr<CFReadStreamRef> stream(AdoptCF, wkCreateCustomCFReadStream(formCreate, formFinalize,
    431         formOpen, formRead, formCanRead, formClose, formSchedule, formUnschedule,
    432         &formContext));
    433     [request setHTTPBodyStream:(NSInputStream *)stream.get()];
    434 }
    435 
    436 FormData* httpBodyFromStream(NSInputStream* stream)
    437 {
    438     MutexLocker locker(streamFieldsMapMutex());
    439     FormStreamFields* formStream = streamFieldsMap().get(reinterpret_cast<CFReadStreamRef>(stream));
    440     if (!formStream)
    441         return 0;
    442     return formStream->formData.get();
     51    return httpBodyFromStream(reinterpret_cast<CFReadStreamRef>(stream));
    44352}
    44453
  • trunk/Source/WebCore/platform/win/FileSystemWin.cpp

    r108353 r112482  
    124124}
    125125
     126#if !USE(CF)
     127
    126128CString fileSystemRepresentation(const String&)
    127129{
     130    ASSERT_NOT_REACHED();
    128131    return "";
    129132}
     133
     134#endif !USE(CF)
    130135
    131136bool makeAllDirectories(const String& path)
Note: See TracChangeset for help on using the changeset viewer.