2008-10-06 Anders Carlsson <andersca@apple.com>
[webkit/qt.git] / WebKit / mac / Plugins / WebBaseNetscapePluginStream.mm
blob57c6fdfae7edb8cb610e543d839b7120a07fa5a6
1 /*
2  * Copyright (C) 2005, 2006, 2007 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  */
29 #if ENABLE(NETSCAPE_PLUGIN_API)
30 #import "WebBaseNetscapePluginStream.h"
32 #import "WebBaseNetscapePluginView.h"
33 #import "WebFrameInternal.h"
34 #import "WebKitErrorsPrivate.h"
35 #import "WebKitLogging.h"
36 #import "WebNSObjectExtras.h"
37 #import "WebNSURLExtras.h"
38 #import "WebNSURLRequestExtras.h"
39 #import "WebNetscapePluginPackage.h"
40 #import "WebNetscapePlugInStreamLoaderClient.h"
41 #import <Foundation/NSURLResponse.h>
42 #import <kjs/JSLock.h>
43 #import <WebCore/DocumentLoader.h>
44 #import <WebCore/Frame.h>
45 #import <WebCore/FrameLoader.h>
46 #import <WebCore/WebCoreObjCExtras.h>
47 #import <WebKitSystemInterface.h>
48 #import <wtf/HashMap.h>
50 using namespace WebCore;
52 #define WEB_REASON_NONE -1
54 static NSString *CarbonPathFromPOSIXPath(NSString *posixPath);
56 typedef HashMap<NPStream*, NPP> StreamMap;
57 static StreamMap& streams()
59     static StreamMap staticStreams;
60     return staticStreams;
63 @implementation WebBaseNetscapePluginStream
65 #ifndef BUILDING_ON_TIGER
66 + (void)initialize
68     WebCoreObjCFinalizeOnMainThread(self);
70 #endif
72 NPP WebNetscapePluginStream::ownerForStream(NPStream *stream)
74     return streams().get(stream);
77 NPReason WebNetscapePluginStream::reasonForError(NSError *error)
79     if (!error)
80         return NPRES_DONE;
82     if ([[error domain] isEqualToString:NSURLErrorDomain] && [error code] == NSURLErrorCancelled)
83         return NPRES_USER_BREAK;
85     return NPRES_NETWORK_ERR;
88 NSError *WebNetscapePluginStream::pluginCancelledConnectionError() const
90     return [[[NSError alloc] _initWithPluginErrorCode:WebKitErrorPlugInCancelledConnection
91                                            contentURL:m_responseURL ? m_responseURL.get() : m_requestURL.get()
92                                         pluginPageURL:nil
93                                            pluginName:[[m_pluginView.get() pluginPackage] name]
94                                              MIMEType:m_mimeType.get()] autorelease];
97 NSError *WebNetscapePluginStream::errorForReason(NPReason reason) const
99     if (reason == NPRES_DONE)
100         return nil;
102     if (reason == NPRES_USER_BREAK)
103         return [NSError _webKitErrorWithDomain:NSURLErrorDomain
104                                           code:NSURLErrorCancelled 
105                                            URL:m_responseURL ? m_responseURL.get() : m_requestURL.get()];
107     return pluginCancelledConnectionError();
110 - (NSError *)errorForReason:(NPReason)theReason
112     return _impl->errorForReason(theReason);
115 - (id)initWithFrameLoader:(FrameLoader *)frameLoader
117     [super init];
118     
119     _impl = WebNetscapePluginStream::create(self);
120     _impl->m_frameLoader = frameLoader;
121     
122     return self;
125 - (id)initWithRequest:(NSURLRequest *)theRequest
126                plugin:(NPP)thePlugin
127            notifyData:(void *)theNotifyData 
128      sendNotification:(BOOL)flag
129 {   
130     WebBaseNetscapePluginView *view = (WebBaseNetscapePluginView *)thePlugin->ndata;
131     
132     // This check has already been done by the plug-in view.
133     ASSERT(FrameLoader::canLoad([theRequest URL], String(), core([view webFrame])->document()));
134     
135     ASSERT([theRequest URL]);
136     ASSERT(thePlugin);
137     
138     _impl = WebNetscapePluginStream::create(self);
139     
140     // Temporarily set isTerminated to true to avoid assertion failure in dealloc in case we are released in this method.
141     _impl->m_isTerminated = true;
142     
143     _impl->m_requestURL = [theRequest URL];
144     _impl->setPlugin(thePlugin);
145     _impl->m_notifyData = theNotifyData;
146     _impl->m_sendNotification = flag;
147     _impl->m_fileDescriptor = -1;
148     _impl->m_newStreamSuccessful = false;
149     
150     streams().add(&_impl->m_stream, thePlugin);
151         
152     _impl->m_request = [theRequest mutableCopy];
153     if (core([view webFrame])->loader()->shouldHideReferrer([theRequest URL], core([view webFrame])->loader()->outgoingReferrer()))
154         [(NSMutableURLRequest *)_impl->m_request _web_setHTTPReferrer:nil];
155     
156     _impl->m_client = new WebNetscapePlugInStreamLoaderClient(self);
157     _impl->m_loader = NetscapePlugInStreamLoader::create(core([view webFrame]), _impl->m_client).releaseRef();
158     _impl->m_loader->setShouldBufferData(false);
159     
160     _impl->m_isTerminated = false;
161     
162     return self;
165 - (void)dealloc
167     ASSERT(!_impl->m_plugin);
168     ASSERT(_impl->m_isTerminated);
169     ASSERT(_impl->m_stream.ndata == nil);
171     // The stream file should have been deleted, and the path freed, in -_destroyStream
172     ASSERT(!_impl->m_path);
173     ASSERT(_impl->m_fileDescriptor == -1);
175     if (_impl->m_loader)
176         _impl->m_loader->deref();
177     delete _impl->m_client;
178     [_impl->m_request release];
179         
180     free((void *)_impl->m_stream.url);
181     free(_impl->m_headers);
183     streams().remove(&_impl->m_stream);
185     ASSERT(_impl);
186     
187     [super dealloc];
190 - (void)finalize
192     ASSERT_MAIN_THREAD();
193     ASSERT(_impl->m_isTerminated);
194     ASSERT(_impl->m_stream.ndata == nil);
196     // The stream file should have been deleted, and the path freed, in -_destroyStream
197     ASSERT(!_impl->m_path);
198     ASSERT(_impl->m_fileDescriptor == -1);
200     if (_impl->m_loader)
201         _impl->m_loader->deref();
202     delete _impl->m_client;
203     
204     free((void *)_impl->m_stream.url);
205     free(_impl->m_headers);
207     streams().remove(&_impl->m_stream);
209     ASSERT(_impl);
210         
211     [super finalize];
214 - (NPP)plugin
216     return _impl->m_plugin;
219 - (void)setRequestURL:(NSURL *)theRequestURL
221     _impl->m_requestURL = theRequestURL;
224 - (void)setPlugin:(NPP)thePlugin
226     _impl->setPlugin(thePlugin);
229 void WebNetscapePluginStream::setPlugin(NPP plugin)
231     if (plugin) {
232         m_plugin = plugin;
233         m_pluginView = static_cast<WebBaseNetscapePluginView *>(m_plugin->ndata);
235         WebNetscapePluginPackage *pluginPackage = [m_pluginView.get() pluginPackage];
236         
237         m_pluginFuncs = [pluginPackage pluginFuncs];
238     } else {
239         WebBaseNetscapePluginView *view = m_pluginView.get();
240         m_plugin = 0;
241         m_pluginFuncs = 0;
242         
243         [view disconnectStream:m_pluginStream];
244         m_pluginView = 0;
245     }        
248 void WebNetscapePluginStream::startStream(NSURL *url, long long expectedContentLength, NSDate *lastModifiedDate, NSString *mimeType, NSData *headers)
250     ASSERT(!m_isTerminated);
251     
252     m_responseURL = url;
253     m_mimeType = mimeType;
254     
255     free((void *)m_stream.url);
256     m_stream.url = strdup([m_responseURL.get() _web_URLCString]);
258     m_stream.ndata = m_pluginStream;
259     m_stream.end = expectedContentLength > 0 ? (uint32)expectedContentLength : 0;
260     m_stream.lastmodified = (uint32)[lastModifiedDate timeIntervalSince1970];
261     m_stream.notifyData = m_notifyData;
263     if (headers) {
264         unsigned len = [headers length];
265         m_headers = (char*) malloc(len + 1);
266         [headers getBytes:m_headers];
267         m_headers[len] = 0;
268         m_stream.headers = m_headers;
269     }
270     
271     m_transferMode = NP_NORMAL;
272     m_offset = 0;
273     m_reason = WEB_REASON_NONE;
274     // FIXME: If WebNetscapePluginStream called our initializer we wouldn't have to do this here.
275     m_fileDescriptor = -1;
277     // FIXME: Need a way to check if stream is seekable
279     [m_pluginView.get() willCallPlugInFunction];
280     NPError npErr = m_pluginFuncs->newstream(m_plugin, (char *)[m_mimeType.get() UTF8String], &m_stream, NO, &m_transferMode);
281     [m_pluginView.get() didCallPlugInFunction];
282     LOG(Plugins, "NPP_NewStream URL=%@ MIME=%@ error=%d", m_responseURL.get(), m_mimeType.get(), npErr);
284     if (npErr != NPERR_NO_ERROR) {
285         LOG_ERROR("NPP_NewStream failed with error: %d responseURL: %@", npErr, m_responseURL.get());
286         // Calling cancelLoadWithError: cancels the load, but doesn't call NPP_DestroyStream.
287         cancelLoadWithError(pluginCancelledConnectionError());
288         return;
289     }
291     m_newStreamSuccessful = true;
293     switch (m_transferMode) {
294         case NP_NORMAL:
295             LOG(Plugins, "Stream type: NP_NORMAL");
296             break;
297         case NP_ASFILEONLY:
298             LOG(Plugins, "Stream type: NP_ASFILEONLY");
299             break;
300         case NP_ASFILE:
301             LOG(Plugins, "Stream type: NP_ASFILE");
302             break;
303         case NP_SEEK:
304             LOG_ERROR("Stream type: NP_SEEK not yet supported");
305             cancelLoadAndDestroyStreamWithError(pluginCancelledConnectionError());
306             break;
307         default:
308             LOG_ERROR("unknown stream type");
309     }
312 - (void)start
314     ASSERT(_impl->m_request);
315     ASSERT(!_impl->m_frameLoader);
316     
317     _impl->m_loader->documentLoader()->addPlugInStreamLoader(_impl->m_loader);
318     _impl->m_loader->load(_impl->m_request);    
321 - (void)startStreamWithResponse:(NSURLResponse *)r
323     NSMutableData *theHeaders = nil;
324     long long expectedContentLength = [r expectedContentLength];
326     if ([r isKindOfClass:[NSHTTPURLResponse class]]) {
327         NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)r;
328         theHeaders = [NSMutableData dataWithCapacity:1024];
329         
330         // FIXME: it would be nice to be able to get the raw HTTP header block.
331         // This includes the HTTP version, the real status text,
332         // all headers in their original order and including duplicates,
333         // and all original bytes verbatim, rather than sent through Unicode translation.
334         // Unfortunately NSHTTPURLResponse doesn't provide access at that low a level.
335         
336         [theHeaders appendBytes:"HTTP " length:5];
337         char statusStr[10];
338         long statusCode = [httpResponse statusCode];
339         snprintf(statusStr, sizeof(statusStr), "%ld", statusCode);
340         [theHeaders appendBytes:statusStr length:strlen(statusStr)];
341         [theHeaders appendBytes:" OK\n" length:4];
343         // HACK: pass the headers through as UTF-8.
344         // This is not the intended behavior; we're supposed to pass original bytes verbatim.
345         // But we don't have the original bytes, we have NSStrings built by the URL loading system.
346         // It hopefully shouldn't matter, since RFC2616/RFC822 require ASCII-only headers,
347         // but surely someone out there is using non-ASCII characters, and hopefully UTF-8 is adequate here.
348         // It seems better than NSASCIIStringEncoding, which will lose information if non-ASCII is used.
350         NSDictionary *headerDict = [httpResponse allHeaderFields];
351         NSArray *keys = [[headerDict allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
352         NSEnumerator *i = [keys objectEnumerator];
353         NSString *k;
354         while ((k = [i nextObject]) != nil) {
355             NSString *v = [headerDict objectForKey:k];
356             [theHeaders appendData:[k dataUsingEncoding:NSUTF8StringEncoding]];
357             [theHeaders appendBytes:": " length:2];
358             [theHeaders appendData:[v dataUsingEncoding:NSUTF8StringEncoding]];
359             [theHeaders appendBytes:"\n" length:1];
360         }
362         // If the content is encoded (most likely compressed), then don't send its length to the plugin,
363         // which is only interested in the decoded length, not yet known at the moment.
364         // <rdar://problem/4470599> tracks a request for -[NSURLResponse expectedContentLength] to incorporate this logic.
365         NSString *contentEncoding = (NSString *)[[(NSHTTPURLResponse *)r allHeaderFields] objectForKey:@"Content-Encoding"];
366         if (contentEncoding && ![contentEncoding isEqualToString:@"identity"])
367             expectedContentLength = -1;
369         // startStreamResponseURL:... will null-terminate.
370     }
372     _impl->startStream([r URL], expectedContentLength, WKGetNSURLResponseLastModifiedDate(r), [r MIMEType], theHeaders);
375 - (BOOL)wantsAllStreams
377     if (!_impl->m_pluginFuncs->getvalue)
378         return NO;
379     
380     void *value = 0;
381     NPError error;
382     WebBaseNetscapePluginView *pv = _impl->m_pluginView.get();
383     [pv willCallPlugInFunction];
384     {
385         JSC::JSLock::DropAllLocks dropAllLocks(false);
386         error = _impl->m_pluginFuncs->getvalue(_impl->m_plugin, NPPVpluginWantsAllNetworkStreams, &value);
387     }
388     [pv didCallPlugInFunction];
389     if (error != NPERR_NO_ERROR)
390         return NO;
391     
392     return value != 0;
395 void WebNetscapePluginStream::destroyStream()
397     if (m_isTerminated)
398         return;
400     RetainPtr<WebBaseNetscapePluginStream> protect(m_pluginStream);
402     ASSERT(m_reason != WEB_REASON_NONE);
403     ASSERT([m_deliveryData.get() length] == 0);
404     
405     m_deliverDataTimer.stop();
407     if (m_stream.ndata != nil) {
408         if (m_reason == NPRES_DONE && (m_transferMode == NP_ASFILE || m_transferMode == NP_ASFILEONLY)) {
409             ASSERT(m_fileDescriptor == -1);
410             ASSERT(m_path);
411             NSString *carbonPath = CarbonPathFromPOSIXPath(m_path.get());
412             ASSERT(carbonPath != NULL);
413             [m_pluginView.get() willCallPlugInFunction];
414             m_pluginFuncs->asfile(m_plugin, &m_stream, [carbonPath fileSystemRepresentation]);
415             [m_pluginView.get() didCallPlugInFunction];
416             LOG(Plugins, "NPP_StreamAsFile responseURL=%@ path=%s", m_responseURL.get(), carbonPath);
417         }
419         if (m_path) {
420             // Delete the file after calling NPP_StreamAsFile(), instead of in -dealloc/-finalize.  It should be OK
421             // to delete the file here -- NPP_StreamAsFile() is always called immediately before NPP_DestroyStream()
422             // (the stream destruction function), so there can be no expectation that a plugin will read the stream
423             // file asynchronously after NPP_StreamAsFile() is called.
424             unlink([m_path.get() fileSystemRepresentation]);
425             m_path = 0;
427             if (m_isTerminated)
428                 return;
429         }
431         if (m_fileDescriptor != -1) {
432             // The file may still be open if we are destroying the stream before it completed loading.
433             close(m_fileDescriptor);
434             m_fileDescriptor = -1;
435         }
437         if (m_newStreamSuccessful) {
438             [m_pluginView.get() willCallPlugInFunction];
439 #if !LOG_DISABLED
440             NPError npErr = 
441 #endif
442             m_pluginFuncs->destroystream(m_plugin, &m_stream, m_reason);
443             [m_pluginView.get() didCallPlugInFunction];
444             LOG(Plugins, "NPP_DestroyStream responseURL=%@ error=%d", m_responseURL.get(), npErr);
445         }
447         free(m_headers);
448         m_headers = NULL;
449         m_stream.headers = NULL;
451         m_stream.ndata = nil;
453         if (m_isTerminated)
454             return;
455     }
457     if (m_sendNotification) {
458         // NPP_URLNotify expects the request URL, not the response URL.
459         [m_pluginView.get() willCallPlugInFunction];
460         m_pluginFuncs->urlnotify(m_plugin, [m_requestURL.get() _web_URLCString], m_reason, m_notifyData);
461         [m_pluginView.get() didCallPlugInFunction];
462         LOG(Plugins, "NPP_URLNotify requestURL=%@ reason=%d", m_requestURL.get(), m_reason);
463     }
465     m_isTerminated = true;
467     setPlugin(0);
470 void WebNetscapePluginStream::destroyStreamWithReason(NPReason reason)
472     m_reason = reason;
473     if (m_reason != NPRES_DONE) {
474         // Stop any pending data from being streamed.
475         [m_deliveryData.get() setLength:0];
476     } else if ([m_deliveryData.get() length] > 0) {
477         // There is more data to be streamed, don't destroy the stream now.
478         return;
479     }
480     destroyStream();
481     ASSERT(m_stream.ndata == nil);
484 void WebNetscapePluginStream::cancelLoadWithError(NSError *error)
486     if (m_frameLoader) {
487         ASSERT(!m_loader);
488         
489         DocumentLoader* documentLoader = m_frameLoader->activeDocumentLoader();
490         ASSERT(documentLoader);
491         
492         if (documentLoader->isLoadingMainResource())
493             documentLoader->cancelMainResourceLoad(error);
494         return;
495     }
496     
497     if (!m_loader->isDone())
498         m_loader->cancel(error);
501 - (void)cancelLoadWithError:(NSError *)error
503     _impl->cancelLoadWithError(error);
506 void WebNetscapePluginStream::destroyStreamWithError(NSError *error)
508     destroyStreamWithReason(reasonForError(error));
511 - (void)destroyStreamWithError:(NSError *)error
513     _impl->destroyStreamWithError(error);
516 void WebNetscapePluginStream::cancelLoadAndDestroyStreamWithError(NSError *error)
518     RetainPtr<WebBaseNetscapePluginStream> protect(m_pluginStream);
519     cancelLoadWithError(error);
520     destroyStreamWithError(error);
521     setPlugin(0);
522 }    
524 - (void)cancelLoadAndDestroyStreamWithError:(NSError *)error
526     return _impl->cancelLoadAndDestroyStreamWithError(error);
529 void WebNetscapePluginStream::deliverData()
531     if (!m_stream.ndata || [m_deliveryData.get() length] == 0)
532         return;
534     RetainPtr<WebBaseNetscapePluginStream> protect(m_pluginStream);
536     int32 totalBytes = [m_deliveryData.get() length];
537     int32 totalBytesDelivered = 0;
539     while (totalBytesDelivered < totalBytes) {
540         [m_pluginView.get() willCallPlugInFunction];
541         int32 deliveryBytes = m_pluginFuncs->writeready(m_plugin, &m_stream);
542         [m_pluginView.get() didCallPlugInFunction];
543         LOG(Plugins, "NPP_WriteReady responseURL=%@ bytes=%d", m_responseURL.get(), deliveryBytes);
545         if (m_isTerminated)
546             return;
548         if (deliveryBytes <= 0) {
549             // Plug-in can't receive anymore data right now. Send it later.
550             if (!m_deliverDataTimer.isActive())
551                 m_deliverDataTimer.startOneShot(0);
552             break;
553         } else {
554             deliveryBytes = MIN(deliveryBytes, totalBytes - totalBytesDelivered);
555             NSData *subdata = [m_deliveryData.get() subdataWithRange:NSMakeRange(totalBytesDelivered, deliveryBytes)];
556             [m_pluginView.get() willCallPlugInFunction];
557             deliveryBytes = m_pluginFuncs->write(m_plugin, &m_stream, m_offset, [subdata length], (void *)[subdata bytes]);
558             [m_pluginView.get() didCallPlugInFunction];
559             if (deliveryBytes < 0) {
560                 // Netscape documentation says that a negative result from NPP_Write means cancel the load.
561                 cancelLoadAndDestroyStreamWithError(pluginCancelledConnectionError());
562                 return;
563             }
564             deliveryBytes = MIN((unsigned)deliveryBytes, [subdata length]);
565             m_offset += deliveryBytes;
566             totalBytesDelivered += deliveryBytes;
567             LOG(Plugins, "NPP_Write responseURL=%@ bytes=%d total-delivered=%d/%d", m_responseURL.get(), deliveryBytes, m_offset, m_stream.end);
568         }
569     }
571     if (totalBytesDelivered > 0) {
572         if (totalBytesDelivered < totalBytes) {
573             NSMutableData *newDeliveryData = [[NSMutableData alloc] initWithCapacity:totalBytes - totalBytesDelivered];
574             [newDeliveryData appendBytes:(char *)[m_deliveryData.get() bytes] + totalBytesDelivered length:totalBytes - totalBytesDelivered];
575             [m_deliveryData.get() release];
576             m_deliveryData = newDeliveryData;
577             [newDeliveryData release];
578         } else {
579             [m_deliveryData.get() setLength:0];
580             if (m_reason != WEB_REASON_NONE) 
581                 destroyStream();
582         }
583     }
586 void WebNetscapePluginStream::deliverDataTimerFired(WebCore::Timer<WebNetscapePluginStream>* timer)
588     deliverData();
591 void WebNetscapePluginStream::deliverDataToFile(NSData *data)
593     if (m_fileDescriptor == -1 && !m_path) {
594         NSString *temporaryFileMask = [NSTemporaryDirectory() stringByAppendingPathComponent:@"WebKitPlugInStreamXXXXXX"];
595         char *temporaryFileName = strdup([temporaryFileMask fileSystemRepresentation]);
596         m_fileDescriptor = mkstemp(temporaryFileName);
597         if (m_fileDescriptor == -1) {
598             LOG_ERROR("Can't create a temporary file.");
599             // This is not a network error, but the only error codes are "network error" and "user break".
600             destroyStreamWithReason(NPRES_NETWORK_ERR);
601             free(temporaryFileName);
602             return;
603         }
605         m_path.adoptNS([[NSString stringWithUTF8String:temporaryFileName] retain]);
606         free(temporaryFileName);
607     }
609     int dataLength = [data length];
610     if (!dataLength)
611         return;
613     int byteCount = write(m_fileDescriptor, [data bytes], dataLength);
614     if (byteCount != dataLength) {
615         // This happens only rarely, when we are out of disk space or have a disk I/O error.
616         LOG_ERROR("error writing to temporary file, errno %d", errno);
617         close(m_fileDescriptor);
618         m_fileDescriptor = -1;
620         // This is not a network error, but the only error codes are "network error" and "user break".
621         destroyStreamWithReason(NPRES_NETWORK_ERR);
622         m_path = 0;
623     }
626 - (void)finishedLoading
628     if (!_impl->m_stream.ndata)
629         return;
631     if (_impl->m_transferMode == NP_ASFILE || _impl->m_transferMode == NP_ASFILEONLY) {
632         // Fake the delivery of an empty data to ensure that the file has been created
633         _impl->deliverDataToFile([NSData data]);
634         if (_impl->m_fileDescriptor != -1)
635             close(_impl->m_fileDescriptor);
636         _impl->m_fileDescriptor = -1;
637     }
639     _impl->destroyStreamWithReason(NPRES_DONE);
642 - (void)receivedData:(NSData *)data
644     ASSERT([data length] > 0);
645     
646     if (_impl->m_transferMode != NP_ASFILEONLY) {
647         if (!_impl->m_deliveryData)
648             _impl->m_deliveryData.adoptNS([[NSMutableData alloc] initWithCapacity:[data length]]);
649         [_impl->m_deliveryData.get() appendData:data];
650         _impl->deliverData();
651     }
652     if (_impl->m_transferMode == NP_ASFILE || _impl->m_transferMode == NP_ASFILEONLY)
653         _impl->deliverDataToFile(data);
656 @end
658 static NSString *CarbonPathFromPOSIXPath(NSString *posixPath)
660     // Doesn't add a trailing colon for directories; this is a problem for paths to a volume,
661     // so this function would need to be revised if we ever wanted to call it with that.
663     CFURLRef url = (CFURLRef)[NSURL fileURLWithPath:posixPath];
664     if (!url)
665         return nil;
667     return WebCFAutorelease(CFURLCopyFileSystemPath(url, kCFURLHFSPathStyle));
670 #endif