OSDN Git Service

Merge WebKit at r78450: Initial merge by git.
[android-x86/external-webkit.git] / Source / WebCore / platform / network / mac / FormDataStreamMac.mm
1 /*
2  * Copyright (C) 2005, 2006, 2008 Apple Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer. 
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution. 
13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission. 
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 /* originally written by Becky Willrich, additional code by Darin Adler */
30
31 #import "config.h"
32 #import "FormDataStreamMac.h"
33
34 #if !USE(CFNETWORK)
35
36 #import "BlobRegistryImpl.h"
37 #import "FileSystem.h"
38 #import "FormData.h"
39 #import "ResourceHandle.h"
40 #import "ResourceHandleClient.h"
41 #import "SchedulePair.h"
42 #import "WebCoreSystemInterface.h"
43 #import <sys/stat.h>
44 #import <sys/types.h>
45 #import <wtf/Assertions.h>
46 #import <wtf/HashMap.h>
47 #import <wtf/MainThread.h>
48 #import <wtf/StdLibExtras.h>
49 #import <wtf/Threading.h>
50
51 #if PLATFORM(IOS)
52 #import <MacErrors.h>
53 #else
54 #import <CoreServices/CoreServices.h>
55 #endif
56
57 namespace WebCore {
58
59 typedef HashMap<CFReadStreamRef, RefPtr<FormData> > StreamFormDataMap;
60 static StreamFormDataMap& getStreamFormDataMap()
61 {
62     DEFINE_STATIC_LOCAL(StreamFormDataMap, streamFormDataMap, ());
63     return streamFormDataMap;
64 }
65
66 typedef HashMap<CFReadStreamRef, RefPtr<ResourceHandle> > StreamResourceHandleMap;
67 static StreamResourceHandleMap& getStreamResourceHandleMap()
68 {
69     DEFINE_STATIC_LOCAL(StreamResourceHandleMap, streamResourceHandleMap, ());
70     return streamResourceHandleMap;
71 }
72
73 void associateStreamWithResourceHandle(NSInputStream *stream, ResourceHandle* resourceHandle)
74 {
75     ASSERT(isMainThread());
76
77     ASSERT(resourceHandle);
78
79     if (!stream)
80         return;
81
82     if (!getStreamFormDataMap().contains((CFReadStreamRef)stream))
83         return;
84
85     ASSERT(!getStreamResourceHandleMap().contains((CFReadStreamRef)stream));
86     getStreamResourceHandleMap().set((CFReadStreamRef)stream, resourceHandle);
87 }
88
89 void disassociateStreamWithResourceHandle(NSInputStream *stream)
90 {
91     ASSERT(isMainThread());
92
93     if (!stream)
94         return;
95
96     getStreamResourceHandleMap().remove((CFReadStreamRef)stream);
97 }
98
99 struct DidSendDataCallbackData {
100     DidSendDataCallbackData(CFReadStreamRef stream_, unsigned long long bytesSent_, unsigned long long streamLength_)
101         : stream(stream_)
102         , bytesSent(bytesSent_)
103         , streamLength(streamLength_)
104     {
105     }
106
107     CFReadStreamRef stream;
108     unsigned long long bytesSent;
109     unsigned long long streamLength;
110 };
111
112 static void performDidSendDataCallback(void* context)
113 {
114     ASSERT(isMainThread());
115
116     DidSendDataCallbackData* data = static_cast<DidSendDataCallbackData*>(context);
117     ResourceHandle* resourceHandle = getStreamResourceHandleMap().get(data->stream).get();
118
119     if (resourceHandle && resourceHandle->client())
120         resourceHandle->client()->didSendData(resourceHandle, data->bytesSent, data->streamLength);
121
122     delete data;
123 }
124
125 static void formEventCallback(CFReadStreamRef stream, CFStreamEventType type, void* context);
126
127 struct FormContext {
128     FormData* formData;
129     unsigned long long streamLength;
130 };
131
132 struct FormStreamFields {
133     SchedulePairHashSet scheduledRunLoopPairs;
134     Vector<FormDataElement> remainingElements; // in reverse order
135     CFReadStreamRef currentStream;
136 #if ENABLE(BLOB)
137     long long currentStreamRangeLength;
138 #endif
139     char* currentData;
140     CFReadStreamRef formStream;
141     unsigned long long streamLength;
142     unsigned long long bytesSent;
143 };
144
145 static void closeCurrentStream(FormStreamFields *form)
146 {
147     if (form->currentStream) {
148         CFReadStreamClose(form->currentStream);
149         CFReadStreamSetClient(form->currentStream, kCFStreamEventNone, NULL, NULL);
150         CFRelease(form->currentStream);
151         form->currentStream = NULL;
152 #if ENABLE(BLOB)
153         form->currentStreamRangeLength = BlobDataItem::toEndOfFile;
154 #endif
155     }
156     if (form->currentData) {
157         fastFree(form->currentData);
158         form->currentData = 0;
159     }
160 }
161
162 // 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.
163 static bool advanceCurrentStream(FormStreamFields* form)
164 {
165     closeCurrentStream(form);
166
167     if (form->remainingElements.isEmpty())
168         return true;
169
170     // Create the new stream.
171     FormDataElement& nextInput = form->remainingElements.last();
172
173     if (nextInput.m_type == FormDataElement::data) {
174         size_t size = nextInput.m_data.size();
175         char* data = nextInput.m_data.releaseBuffer();
176         form->currentStream = CFReadStreamCreateWithBytesNoCopy(0, reinterpret_cast<const UInt8*>(data), size, kCFAllocatorNull);
177         form->currentData = data;
178     } else {
179 #if ENABLE(BLOB)
180         // Check if the file has been changed or not if required.
181         if (nextInput.m_expectedFileModificationTime != BlobDataItem::doNotCheckFileChange) {
182             time_t fileModificationTime;
183             if (!getFileModificationTime(nextInput.m_filename, fileModificationTime) || fileModificationTime != static_cast<time_t>(nextInput.m_expectedFileModificationTime))
184                 return false;
185         }
186 #endif
187         const String& path = nextInput.m_shouldGenerateFile ? nextInput.m_generatedFilename : nextInput.m_filename;
188         form->currentStream = CFReadStreamCreateWithFile(0, pathAsURL(path).get());
189         if (!form->currentStream) {
190             // The file must have been removed or become unreadable.
191             return false;
192         }
193 #if ENABLE(BLOB)
194         if (nextInput.m_fileStart > 0) {
195             CFNumberRef position = CFNumberCreate(0, kCFNumberLongLongType, &nextInput.m_fileStart);
196             CFReadStreamSetProperty(form->currentStream, kCFStreamPropertyFileCurrentOffset, position);
197         }
198         form->currentStreamRangeLength = nextInput.m_fileLength;
199 #endif
200     }
201     form->remainingElements.removeLast();
202
203     // Set up the callback.
204     CFStreamClientContext context = { 0, form, NULL, NULL, NULL };
205     CFReadStreamSetClient(form->currentStream, kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered,
206         formEventCallback, &context);
207
208     // Schedule with the current set of run loops.
209     SchedulePairHashSet::iterator end = form->scheduledRunLoopPairs.end();
210     for (SchedulePairHashSet::iterator it = form->scheduledRunLoopPairs.begin(); it != end; ++it)
211         CFReadStreamScheduleWithRunLoop(form->currentStream, (*it)->runLoop(), (*it)->mode());
212
213     return true;
214 }
215
216 static bool openNextStream(FormStreamFields* form)
217 {
218     // Skip over any streams we can't open.
219     if (!advanceCurrentStream(form))
220         return false;
221     while (form->currentStream && !CFReadStreamOpen(form->currentStream)) {
222         if (!advanceCurrentStream(form))
223             return false;
224     }
225     return true;
226 }
227
228 static void* formCreate(CFReadStreamRef stream, void* context)
229 {
230     FormContext* formContext = static_cast<FormContext*>(context);
231
232     FormStreamFields* newInfo = new FormStreamFields;
233     newInfo->currentStream = NULL;
234 #if ENABLE(BLOB)
235     newInfo->currentStreamRangeLength = BlobDataItem::toEndOfFile;
236 #endif
237     newInfo->currentData = 0;
238     newInfo->formStream = stream; // Don't retain. That would create a reference cycle.
239     newInfo->streamLength = formContext->streamLength;
240     newInfo->bytesSent = 0;
241
242     FormData* formData = formContext->formData;
243
244     // Append in reverse order since we remove elements from the end.
245     size_t size = formData->elements().size();
246     newInfo->remainingElements.reserveInitialCapacity(size);
247     for (size_t i = 0; i < size; ++i)
248         newInfo->remainingElements.append(formData->elements()[size - i - 1]);
249
250     getStreamFormDataMap().set(stream, adoptRef(formData));
251
252     return newInfo;
253 }
254
255 static void formFinalize(CFReadStreamRef stream, void* context)
256 {
257     FormStreamFields* form = static_cast<FormStreamFields*>(context);
258
259     getStreamFormDataMap().remove(stream);
260
261     closeCurrentStream(form);
262     delete form;
263 }
264
265 static Boolean formOpen(CFReadStreamRef, CFStreamError* error, Boolean* openComplete, void* context)
266 {
267     FormStreamFields* form = static_cast<FormStreamFields*>(context);
268
269     bool opened = openNextStream(form);
270
271     *openComplete = opened;
272     error->error = opened ? 0 : fnfErr;
273     return opened;
274 }
275
276 static CFIndex formRead(CFReadStreamRef stream, UInt8* buffer, CFIndex bufferLength, CFStreamError* error, Boolean* atEOF, void* context)
277 {
278     FormStreamFields* form = static_cast<FormStreamFields*>(context);
279
280     while (form->currentStream) {
281         CFIndex bytesToRead = bufferLength;
282 #if ENABLE(BLOB)
283         if (form->currentStreamRangeLength != BlobDataItem::toEndOfFile && form->currentStreamRangeLength < bytesToRead)
284             bytesToRead = static_cast<CFIndex>(form->currentStreamRangeLength);
285 #endif
286         CFIndex bytesRead = CFReadStreamRead(form->currentStream, buffer, bytesToRead);
287         if (bytesRead < 0) {
288             *error = CFReadStreamGetError(form->currentStream);
289             return -1;
290         }
291         if (bytesRead > 0) {
292             error->error = 0;
293             *atEOF = FALSE;
294             form->bytesSent += bytesRead;
295 #if ENABLE(BLOB)
296             if (form->currentStreamRangeLength != BlobDataItem::toEndOfFile)
297                 form->currentStreamRangeLength -= bytesRead;
298 #endif
299
300             if (!ResourceHandle::didSendBodyDataDelegateExists()) {
301                 // FIXME: Figure out how to only do this when a ResourceHandleClient is available.
302                 DidSendDataCallbackData* data = new DidSendDataCallbackData(stream, form->bytesSent, form->streamLength);
303                 callOnMainThread(performDidSendDataCallback, data);
304             }
305
306             return bytesRead;
307         }
308         openNextStream(form);
309     }
310
311     error->error = 0;
312     *atEOF = TRUE;
313     return 0;
314 }
315
316 static Boolean formCanRead(CFReadStreamRef stream, void* context)
317 {
318     FormStreamFields* form = static_cast<FormStreamFields*>(context);
319
320     while (form->currentStream && CFReadStreamGetStatus(form->currentStream) == kCFStreamStatusAtEnd) {
321         openNextStream(form);
322     }
323     if (!form->currentStream) {
324         wkSignalCFReadStreamEnd(stream);
325         return FALSE;
326     }
327     return CFReadStreamHasBytesAvailable(form->currentStream);
328 }
329
330 static void formClose(CFReadStreamRef, void* context)
331 {
332     FormStreamFields* form = static_cast<FormStreamFields*>(context);
333
334     closeCurrentStream(form);
335 }
336
337 static void formSchedule(CFReadStreamRef, CFRunLoopRef runLoop, CFStringRef runLoopMode, void* context)
338 {
339     FormStreamFields* form = static_cast<FormStreamFields*>(context);
340
341     if (form->currentStream)
342         CFReadStreamScheduleWithRunLoop(form->currentStream, runLoop, runLoopMode);
343     form->scheduledRunLoopPairs.add(SchedulePair::create(runLoop, runLoopMode));
344 }
345
346 static void formUnschedule(CFReadStreamRef, CFRunLoopRef runLoop, CFStringRef runLoopMode, void* context)
347 {
348     FormStreamFields* form = static_cast<FormStreamFields*>(context);
349
350     if (form->currentStream)
351         CFReadStreamUnscheduleFromRunLoop(form->currentStream, runLoop, runLoopMode);
352     form->scheduledRunLoopPairs.remove(SchedulePair::create(runLoop, runLoopMode));
353 }
354
355 static void formEventCallback(CFReadStreamRef stream, CFStreamEventType type, void* context)
356 {
357     FormStreamFields* form = static_cast<FormStreamFields*>(context);
358
359     switch (type) {
360     case kCFStreamEventHasBytesAvailable:
361         wkSignalCFReadStreamHasBytes(form->formStream);
362         break;
363     case kCFStreamEventErrorOccurred: {
364         CFStreamError readStreamError = CFReadStreamGetError(stream);
365         wkSignalCFReadStreamError(form->formStream, &readStreamError);
366         break;
367     }
368     case kCFStreamEventEndEncountered:
369         openNextStream(form);
370         if (!form->currentStream) {
371             wkSignalCFReadStreamEnd(form->formStream);
372         }
373         break;
374     case kCFStreamEventNone:
375         LOG_ERROR("unexpected kCFStreamEventNone");
376         break;
377     case kCFStreamEventOpenCompleted:
378         LOG_ERROR("unexpected kCFStreamEventOpenCompleted");
379         break;
380     case kCFStreamEventCanAcceptBytes:
381         LOG_ERROR("unexpected kCFStreamEventCanAcceptBytes");
382         break;
383     }
384 }
385
386 void setHTTPBody(NSMutableURLRequest *request, PassRefPtr<FormData> formData)
387 {
388     if (!formData)
389         return;
390         
391     size_t count = formData->elements().size();
392
393     // Handle the common special case of one piece of form data, with no files.
394     if (count == 1 && !formData->alwaysStream()) {
395         const FormDataElement& element = formData->elements()[0];
396         if (element.m_type == FormDataElement::data) {
397             NSData *data = [[NSData alloc] initWithBytes:element.m_data.data() length:element.m_data.size()];
398             [request setHTTPBody:data];
399             [data release];
400             return;
401         }
402     }
403
404 #if ENABLE(BLOB)
405     // Check if there is a blob in the form data.
406     bool hasBlob = false;
407     for (size_t i = 0; i < count; ++i) {
408         const FormDataElement& element = formData->elements()[i];
409         if (element.m_type == FormDataElement::encodedBlob) {
410             hasBlob = true;
411             break;
412         }
413     }
414
415     // If yes, we have to resolve all the blob references and regenerate the form data with only data and file types.
416     if (hasBlob) {
417         RefPtr<FormData> newFormData = FormData::create();
418         newFormData->setAlwaysStream(formData->alwaysStream());
419         newFormData->setIdentifier(formData->identifier());
420         for (size_t i = 0; i < count; ++i) {
421             const FormDataElement& element = formData->elements()[i];
422             if (element.m_type == FormDataElement::data)
423                 newFormData->appendData(element.m_data.data(), element.m_data.size());
424             else if (element.m_type == FormDataElement::encodedFile)
425                 newFormData->appendFile(element.m_filename, element.m_shouldGenerateFile);
426             else {
427                 ASSERT(element.m_type == FormDataElement::encodedBlob);
428                 RefPtr<BlobStorageData> blobData = static_cast<BlobRegistryImpl&>(blobRegistry()).getBlobDataFromURL(KURL(ParsedURLString, element.m_blobURL));
429                 if (blobData) {
430                     for (size_t j = 0; j < blobData->items().size(); ++j) {
431                         const BlobDataItem& blobItem = blobData->items()[j];
432                         if (blobItem.type == BlobDataItem::Data) {
433                             newFormData->appendData(blobItem.data->data() + static_cast<int>(blobItem.offset), static_cast<int>(blobItem.length));
434                         } else {
435                             ASSERT(blobItem.type == BlobDataItem::File);
436                             newFormData->appendFileRange(blobItem.path, blobItem.offset, blobItem.length, blobItem.expectedModificationTime);
437                         }
438                     }
439                 }
440             }
441         }
442         formData = newFormData;
443         count = formData->elements().size();
444     }
445 #endif
446
447     // Precompute the content length so NSURLConnection doesn't use chunked mode.
448     long long length = 0;
449     for (size_t i = 0; i < count; ++i) {
450         const FormDataElement& element = formData->elements()[i];
451         if (element.m_type == FormDataElement::data)
452             length += element.m_data.size();
453         else {
454 #if ENABLE(BLOB)
455             // 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.
456             if (element.m_fileLength != BlobDataItem::toEndOfFile) {
457                 length += element.m_fileLength;
458                 continue;
459             }
460 #endif
461             long long fileSize;
462             if (getFileSize(element.m_shouldGenerateFile ? element.m_generatedFilename : element.m_filename, fileSize))
463                 length += fileSize;
464         }
465     }
466
467     // Set the length.
468     [request setValue:[NSString stringWithFormat:@"%lld", length] forHTTPHeaderField:@"Content-Length"];
469
470     // Create and set the stream.
471
472     // Pass the length along with the formData so it does not have to be recomputed.
473     FormContext formContext = { formData.releaseRef(), length };
474
475     RetainPtr<CFReadStreamRef> stream(AdoptCF, wkCreateCustomCFReadStream(formCreate, formFinalize,
476         formOpen, formRead, formCanRead, formClose, formSchedule, formUnschedule,
477         &formContext));
478     [request setHTTPBodyStream:(NSInputStream *)stream.get()];
479 }
480
481 FormData* httpBodyFromStream(NSInputStream* stream)
482 {
483     return getStreamFormDataMap().get((CFReadStreamRef)stream).get();
484 }
485
486 } // namespace WebCore
487
488 #endif // !USE(CFNETWORK)