--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/WebKit/mac/Plugins/WebNetscapePluginStream.mm Fri Sep 17 09:02:29 2010 +0300
@@ -0,0 +1,643 @@
+/*
+ * Copyright (C) 2005, 2006, 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if ENABLE(NETSCAPE_PLUGIN_API)
+#import "WebNetscapePluginStream.h"
+
+#import "WebNetscapePluginView.h"
+#import "WebFrameInternal.h"
+#import "WebKitErrorsPrivate.h"
+#import "WebKitLogging.h"
+#import "WebNSObjectExtras.h"
+#import "WebNSURLExtras.h"
+#import "WebNSURLRequestExtras.h"
+#import "WebNetscapePluginPackage.h"
+#import <Foundation/NSURLResponse.h>
+#import <runtime/JSLock.h>
+#import <WebCore/DocumentLoader.h>
+#import <WebCore/Frame.h>
+#import <WebCore/FrameLoader.h>
+#import <WebCore/SecurityOrigin.h>
+#import <WebCore/WebCoreObjCExtras.h>
+#import <WebCore/WebCoreURLResponse.h>
+#import <WebKitSystemInterface.h>
+#import <wtf/HashMap.h>
+#import <wtf/StdLibExtras.h>
+
+using namespace WebCore;
+using namespace std;
+
+#define WEB_REASON_NONE -1
+
+static NSString *CarbonPathFromPOSIXPath(NSString *posixPath);
+
+class PluginStopDeferrer {
+public:
+ PluginStopDeferrer(WebNetscapePluginView* pluginView)
+ : m_pluginView(pluginView)
+ {
+ ASSERT(m_pluginView);
+
+ [m_pluginView.get() willCallPlugInFunction];
+ }
+
+ ~PluginStopDeferrer()
+ {
+ ASSERT(m_pluginView);
+ [m_pluginView.get() didCallPlugInFunction];
+ }
+
+private:
+ RetainPtr<WebNetscapePluginView> m_pluginView;
+};
+
+typedef HashMap<NPStream*, NPP> StreamMap;
+static StreamMap& streams()
+{
+ DEFINE_STATIC_LOCAL(StreamMap, staticStreams, ());
+ return staticStreams;
+}
+
+NPP WebNetscapePluginStream::ownerForStream(NPStream *stream)
+{
+ return streams().get(stream);
+}
+
+NPReason WebNetscapePluginStream::reasonForError(NSError *error)
+{
+ if (!error)
+ return NPRES_DONE;
+
+ if ([[error domain] isEqualToString:NSURLErrorDomain] && [error code] == NSURLErrorCancelled)
+ return NPRES_USER_BREAK;
+
+ return NPRES_NETWORK_ERR;
+}
+
+NSError *WebNetscapePluginStream::pluginCancelledConnectionError() const
+{
+ return [[[NSError alloc] _initWithPluginErrorCode:WebKitErrorPlugInCancelledConnection
+ contentURL:m_responseURL ? m_responseURL.get() : (NSURL *)m_requestURL
+ pluginPageURL:nil
+ pluginName:[[m_pluginView.get() pluginPackage] pluginInfo].name
+ MIMEType:(NSString *)String::fromUTF8(m_mimeType.data(), m_mimeType.length())] autorelease];
+}
+
+NSError *WebNetscapePluginStream::errorForReason(NPReason reason) const
+{
+ if (reason == NPRES_DONE)
+ return nil;
+
+ if (reason == NPRES_USER_BREAK)
+ return [NSError _webKitErrorWithDomain:NSURLErrorDomain
+ code:NSURLErrorCancelled
+ URL:m_responseURL ? m_responseURL.get() : (NSURL *)m_requestURL];
+
+ return pluginCancelledConnectionError();
+}
+
+WebNetscapePluginStream::WebNetscapePluginStream(FrameLoader* frameLoader)
+ : m_plugin(0)
+ , m_transferMode(0)
+ , m_offset(0)
+ , m_fileDescriptor(-1)
+ , m_sendNotification(false)
+ , m_notifyData(0)
+ , m_headers(0)
+ , m_reason(NPRES_BASE)
+ , m_isTerminated(false)
+ , m_newStreamSuccessful(false)
+ , m_frameLoader(frameLoader)
+ , m_pluginFuncs(0)
+ , m_deliverDataTimer(this, &WebNetscapePluginStream::deliverDataTimerFired)
+{
+ memset(&m_stream, 0, sizeof(NPStream));
+}
+
+WebNetscapePluginStream::WebNetscapePluginStream(NSURLRequest *request, NPP plugin, bool sendNotification, void* notifyData)
+ : m_requestURL([request URL])
+ , m_plugin(0)
+ , m_transferMode(0)
+ , m_offset(0)
+ , m_fileDescriptor(-1)
+ , m_sendNotification(sendNotification)
+ , m_notifyData(notifyData)
+ , m_headers(0)
+ , m_reason(NPRES_BASE)
+ , m_isTerminated(false)
+ , m_newStreamSuccessful(false)
+ , m_frameLoader(0)
+ , m_request(AdoptNS, [request mutableCopy])
+ , m_pluginFuncs(0)
+ , m_deliverDataTimer(this, &WebNetscapePluginStream::deliverDataTimerFired)
+{
+ memset(&m_stream, 0, sizeof(NPStream));
+
+ WebNetscapePluginView *view = (WebNetscapePluginView *)plugin->ndata;
+
+ // This check has already been done by the plug-in view.
+ ASSERT(SecurityOrigin::canLoad([request URL], String(), core([view webFrame])->document()));
+
+ ASSERT([request URL]);
+ ASSERT(plugin);
+
+ setPlugin(plugin);
+
+ streams().add(&m_stream, plugin);
+
+ if (SecurityOrigin::shouldHideReferrer([request URL], core([view webFrame])->loader()->outgoingReferrer()))
+ [m_request.get() _web_setHTTPReferrer:nil];
+}
+
+WebNetscapePluginStream::~WebNetscapePluginStream()
+{
+ ASSERT(!m_plugin);
+ ASSERT(m_isTerminated);
+ ASSERT(!m_stream.ndata);
+
+ // The stream file should have been deleted, and the path freed, in -_destroyStream
+ ASSERT(!m_path);
+ ASSERT(m_fileDescriptor == -1);
+
+ free((void *)m_stream.url);
+ free(m_headers);
+
+ streams().remove(&m_stream);
+}
+
+void WebNetscapePluginStream::setPlugin(NPP plugin)
+{
+ if (plugin) {
+ m_plugin = plugin;
+ m_pluginView = static_cast<WebNetscapePluginView *>(m_plugin->ndata);
+
+ WebNetscapePluginPackage *pluginPackage = [m_pluginView.get() pluginPackage];
+
+ m_pluginFuncs = [pluginPackage pluginFuncs];
+ } else {
+ WebNetscapePluginView *view = m_pluginView.get();
+ m_plugin = 0;
+ m_pluginFuncs = 0;
+
+ [view disconnectStream:this];
+ m_pluginView = 0;
+ }
+}
+
+void WebNetscapePluginStream::startStream(NSURL *url, long long expectedContentLength, NSDate *lastModifiedDate, const String& mimeType, NSData *headers)
+{
+ ASSERT(!m_isTerminated);
+
+ m_responseURL = url;
+ m_mimeType = mimeType.utf8();
+
+ free((void *)m_stream.url);
+ m_stream.url = strdup([m_responseURL.get() _web_URLCString]);
+
+ m_stream.ndata = this;
+ m_stream.end = expectedContentLength > 0 ? (uint32_t)expectedContentLength : 0;
+ m_stream.lastmodified = (uint32_t)[lastModifiedDate timeIntervalSince1970];
+ m_stream.notifyData = m_notifyData;
+
+ if (headers) {
+ unsigned len = [headers length];
+ m_headers = (char*) malloc(len + 1);
+ [headers getBytes:m_headers];
+ m_headers[len] = 0;
+ m_stream.headers = m_headers;
+ }
+
+ m_transferMode = NP_NORMAL;
+ m_offset = 0;
+ m_reason = WEB_REASON_NONE;
+ // FIXME: If WebNetscapePluginStream called our initializer we wouldn't have to do this here.
+ m_fileDescriptor = -1;
+
+ // FIXME: Need a way to check if stream is seekable
+
+ NPError npErr;
+ {
+ PluginStopDeferrer deferrer(m_pluginView.get());
+ npErr = m_pluginFuncs->newstream(m_plugin, m_mimeType.mutableData(), &m_stream, NO, &m_transferMode);
+ }
+
+ LOG(Plugins, "NPP_NewStream URL=%@ MIME=%s error=%d", m_responseURL.get(), m_mimeType.data(), npErr);
+
+ if (npErr != NPERR_NO_ERROR) {
+ LOG_ERROR("NPP_NewStream failed with error: %d responseURL: %@", npErr, m_responseURL.get());
+ // Calling cancelLoadWithError: cancels the load, but doesn't call NPP_DestroyStream.
+ cancelLoadWithError(pluginCancelledConnectionError());
+ return;
+ }
+
+ m_newStreamSuccessful = true;
+
+ switch (m_transferMode) {
+ case NP_NORMAL:
+ LOG(Plugins, "Stream type: NP_NORMAL");
+ break;
+ case NP_ASFILEONLY:
+ LOG(Plugins, "Stream type: NP_ASFILEONLY");
+ break;
+ case NP_ASFILE:
+ LOG(Plugins, "Stream type: NP_ASFILE");
+ break;
+ case NP_SEEK:
+ LOG_ERROR("Stream type: NP_SEEK not yet supported");
+ cancelLoadAndDestroyStreamWithError(pluginCancelledConnectionError());
+ break;
+ default:
+ LOG_ERROR("unknown stream type");
+ }
+}
+
+void WebNetscapePluginStream::start()
+{
+ ASSERT(m_request);
+ ASSERT(!m_frameLoader);
+ ASSERT(!m_loader);
+
+ m_loader = NetscapePlugInStreamLoader::create(core([m_pluginView.get() webFrame]), this);
+ m_loader->setShouldBufferData(false);
+
+ m_loader->documentLoader()->addPlugInStreamLoader(m_loader.get());
+ m_loader->load(m_request.get());
+}
+
+void WebNetscapePluginStream::stop()
+{
+ ASSERT(!m_frameLoader);
+
+ if (!m_loader->isDone())
+ cancelLoadAndDestroyStreamWithError(m_loader->cancelledError());
+}
+
+void WebNetscapePluginStream::didReceiveResponse(NetscapePlugInStreamLoader*, const ResourceResponse& response)
+{
+ NSURLResponse *r = response.nsURLResponse();
+
+ NSMutableData *theHeaders = nil;
+ long long expectedContentLength = [r expectedContentLength];
+
+ if ([r isKindOfClass:[NSHTTPURLResponse class]]) {
+ NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)r;
+ theHeaders = [NSMutableData dataWithCapacity:1024];
+
+ // FIXME: it would be nice to be able to get the raw HTTP header block.
+ // This includes the HTTP version, the real status text,
+ // all headers in their original order and including duplicates,
+ // and all original bytes verbatim, rather than sent through Unicode translation.
+ // Unfortunately NSHTTPURLResponse doesn't provide access at that low a level.
+
+ [theHeaders appendBytes:"HTTP " length:5];
+ char statusStr[10];
+ long statusCode = [httpResponse statusCode];
+ snprintf(statusStr, sizeof(statusStr), "%ld", statusCode);
+ [theHeaders appendBytes:statusStr length:strlen(statusStr)];
+ [theHeaders appendBytes:" OK\n" length:4];
+
+ // HACK: pass the headers through as UTF-8.
+ // This is not the intended behavior; we're supposed to pass original bytes verbatim.
+ // But we don't have the original bytes, we have NSStrings built by the URL loading system.
+ // It hopefully shouldn't matter, since RFC2616/RFC822 require ASCII-only headers,
+ // but surely someone out there is using non-ASCII characters, and hopefully UTF-8 is adequate here.
+ // It seems better than NSASCIIStringEncoding, which will lose information if non-ASCII is used.
+
+ NSDictionary *headerDict = [httpResponse allHeaderFields];
+ NSArray *keys = [[headerDict allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
+ NSEnumerator *i = [keys objectEnumerator];
+ NSString *k;
+ while ((k = [i nextObject]) != nil) {
+ NSString *v = [headerDict objectForKey:k];
+ [theHeaders appendData:[k dataUsingEncoding:NSUTF8StringEncoding]];
+ [theHeaders appendBytes:": " length:2];
+ [theHeaders appendData:[v dataUsingEncoding:NSUTF8StringEncoding]];
+ [theHeaders appendBytes:"\n" length:1];
+ }
+
+ // If the content is encoded (most likely compressed), then don't send its length to the plugin,
+ // which is only interested in the decoded length, not yet known at the moment.
+ // <rdar://problem/4470599> tracks a request for -[NSURLResponse expectedContentLength] to incorporate this logic.
+ NSString *contentEncoding = (NSString *)[[(NSHTTPURLResponse *)r allHeaderFields] objectForKey:@"Content-Encoding"];
+ if (contentEncoding && ![contentEncoding isEqualToString:@"identity"])
+ expectedContentLength = -1;
+
+ // startStreamResponseURL:... will null-terminate.
+ }
+
+ startStream([r URL], expectedContentLength, WKGetNSURLResponseLastModifiedDate(r), response.mimeType(), theHeaders);
+}
+
+void WebNetscapePluginStream::startStreamWithResponse(NSURLResponse *response)
+{
+ didReceiveResponse(0, response);
+}
+
+bool WebNetscapePluginStream::wantsAllStreams() const
+{
+ if (!m_pluginFuncs->getvalue)
+ return false;
+
+ void *value = 0;
+ NPError error;
+ {
+ PluginStopDeferrer deferrer(m_pluginView.get());
+ JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly);
+ error = m_pluginFuncs->getvalue(m_plugin, NPPVpluginWantsAllNetworkStreams, &value);
+ }
+ if (error != NPERR_NO_ERROR)
+ return false;
+
+ return value;
+}
+
+void WebNetscapePluginStream::destroyStream()
+{
+ if (m_isTerminated)
+ return;
+
+ RefPtr<WebNetscapePluginStream> protect(this);
+
+ ASSERT(m_reason != WEB_REASON_NONE);
+ ASSERT([m_deliveryData.get() length] == 0);
+
+ m_deliverDataTimer.stop();
+
+ if (m_stream.ndata) {
+ if (m_reason == NPRES_DONE && (m_transferMode == NP_ASFILE || m_transferMode == NP_ASFILEONLY)) {
+ ASSERT(m_fileDescriptor == -1);
+ ASSERT(m_path);
+ NSString *carbonPath = CarbonPathFromPOSIXPath(m_path.get());
+ ASSERT(carbonPath != NULL);
+
+ PluginStopDeferrer deferrer(m_pluginView.get());
+ m_pluginFuncs->asfile(m_plugin, &m_stream, [carbonPath fileSystemRepresentation]);
+ LOG(Plugins, "NPP_StreamAsFile responseURL=%@ path=%s", m_responseURL.get(), carbonPath);
+ }
+
+ if (m_path) {
+ // Delete the file after calling NPP_StreamAsFile(), instead of in -dealloc/-finalize. It should be OK
+ // to delete the file here -- NPP_StreamAsFile() is always called immediately before NPP_DestroyStream()
+ // (the stream destruction function), so there can be no expectation that a plugin will read the stream
+ // file asynchronously after NPP_StreamAsFile() is called.
+ unlink([m_path.get() fileSystemRepresentation]);
+ m_path = 0;
+
+ if (m_isTerminated)
+ return;
+ }
+
+ if (m_fileDescriptor != -1) {
+ // The file may still be open if we are destroying the stream before it completed loading.
+ close(m_fileDescriptor);
+ m_fileDescriptor = -1;
+ }
+
+ if (m_newStreamSuccessful) {
+ PluginStopDeferrer deferrer(m_pluginView.get());
+#if !LOG_DISABLED
+ NPError npErr =
+#endif
+ m_pluginFuncs->destroystream(m_plugin, &m_stream, m_reason);
+ LOG(Plugins, "NPP_DestroyStream responseURL=%@ error=%d", m_responseURL.get(), npErr);
+ }
+
+ free(m_headers);
+ m_headers = NULL;
+ m_stream.headers = NULL;
+
+ m_stream.ndata = 0;
+
+ if (m_isTerminated)
+ return;
+ }
+
+ if (m_sendNotification) {
+ // NPP_URLNotify expects the request URL, not the response URL.
+ PluginStopDeferrer deferrer(m_pluginView.get());
+ m_pluginFuncs->urlnotify(m_plugin, m_requestURL.string().utf8().data(), m_reason, m_notifyData);
+ LOG(Plugins, "NPP_URLNotify requestURL=%@ reason=%d", (NSURL *)m_requestURL, m_reason);
+ }
+
+ m_isTerminated = true;
+
+ setPlugin(0);
+}
+
+void WebNetscapePluginStream::destroyStreamWithReason(NPReason reason)
+{
+ m_reason = reason;
+ if (m_reason != NPRES_DONE) {
+ // Stop any pending data from being streamed.
+ [m_deliveryData.get() setLength:0];
+ } else if ([m_deliveryData.get() length] > 0) {
+ // There is more data to be streamed, don't destroy the stream now.
+ return;
+ }
+
+ RefPtr<WebNetscapePluginStream> protect(this);
+ destroyStream();
+ ASSERT(!m_stream.ndata);
+}
+
+void WebNetscapePluginStream::cancelLoadWithError(NSError *error)
+{
+ if (m_frameLoader) {
+ ASSERT(!m_loader);
+
+ DocumentLoader* documentLoader = m_frameLoader->activeDocumentLoader();
+ ASSERT(documentLoader);
+
+ if (documentLoader->isLoadingMainResource())
+ documentLoader->cancelMainResourceLoad(error);
+ return;
+ }
+
+ if (!m_loader->isDone())
+ m_loader->cancel(error);
+}
+
+void WebNetscapePluginStream::destroyStreamWithError(NSError *error)
+{
+ destroyStreamWithReason(reasonForError(error));
+}
+
+void WebNetscapePluginStream::didFail(WebCore::NetscapePlugInStreamLoader*, const WebCore::ResourceError& error)
+{
+ destroyStreamWithError(error);
+}
+
+void WebNetscapePluginStream::cancelLoadAndDestroyStreamWithError(NSError *error)
+{
+ RefPtr<WebNetscapePluginStream> protect(this);
+ cancelLoadWithError(error);
+ destroyStreamWithError(error);
+ setPlugin(0);
+}
+
+void WebNetscapePluginStream::deliverData()
+{
+ if (!m_stream.ndata || [m_deliveryData.get() length] == 0)
+ return;
+
+ RefPtr<WebNetscapePluginStream> protect(this);
+
+ int32_t totalBytes = [m_deliveryData.get() length];
+ int32_t totalBytesDelivered = 0;
+
+ while (totalBytesDelivered < totalBytes) {
+ PluginStopDeferrer deferrer(m_pluginView.get());
+ int32_t deliveryBytes = m_pluginFuncs->writeready(m_plugin, &m_stream);
+ LOG(Plugins, "NPP_WriteReady responseURL=%@ bytes=%d", m_responseURL.get(), deliveryBytes);
+
+ if (m_isTerminated)
+ return;
+
+ if (deliveryBytes <= 0) {
+ // Plug-in can't receive anymore data right now. Send it later.
+ if (!m_deliverDataTimer.isActive())
+ m_deliverDataTimer.startOneShot(0);
+ break;
+ } else {
+ deliveryBytes = min(deliveryBytes, totalBytes - totalBytesDelivered);
+ NSData *subdata = [m_deliveryData.get() subdataWithRange:NSMakeRange(totalBytesDelivered, deliveryBytes)];
+ PluginStopDeferrer deferrer(m_pluginView.get());
+ deliveryBytes = m_pluginFuncs->write(m_plugin, &m_stream, m_offset, [subdata length], (void *)[subdata bytes]);
+ if (deliveryBytes < 0) {
+ // Netscape documentation says that a negative result from NPP_Write means cancel the load.
+ cancelLoadAndDestroyStreamWithError(pluginCancelledConnectionError());
+ return;
+ }
+ deliveryBytes = min<int32_t>(deliveryBytes, [subdata length]);
+ m_offset += deliveryBytes;
+ totalBytesDelivered += deliveryBytes;
+ LOG(Plugins, "NPP_Write responseURL=%@ bytes=%d total-delivered=%d/%d", m_responseURL.get(), deliveryBytes, m_offset, m_stream.end);
+ }
+ }
+
+ if (totalBytesDelivered > 0) {
+ if (totalBytesDelivered < totalBytes) {
+ NSMutableData *newDeliveryData = [[NSMutableData alloc] initWithCapacity:totalBytes - totalBytesDelivered];
+ [newDeliveryData appendBytes:(char *)[m_deliveryData.get() bytes] + totalBytesDelivered length:totalBytes - totalBytesDelivered];
+
+ m_deliveryData.adoptNS(newDeliveryData);
+ } else {
+ [m_deliveryData.get() setLength:0];
+ if (m_reason != WEB_REASON_NONE)
+ destroyStream();
+ }
+ }
+}
+
+void WebNetscapePluginStream::deliverDataTimerFired(WebCore::Timer<WebNetscapePluginStream>* timer)
+{
+ deliverData();
+}
+
+void WebNetscapePluginStream::deliverDataToFile(NSData *data)
+{
+ if (m_fileDescriptor == -1 && !m_path) {
+ NSString *temporaryFileMask = [NSTemporaryDirectory() stringByAppendingPathComponent:@"WebKitPlugInStreamXXXXXX"];
+ char *temporaryFileName = strdup([temporaryFileMask fileSystemRepresentation]);
+ m_fileDescriptor = mkstemp(temporaryFileName);
+ if (m_fileDescriptor == -1) {
+ LOG_ERROR("Can't create a temporary file.");
+ // This is not a network error, but the only error codes are "network error" and "user break".
+ destroyStreamWithReason(NPRES_NETWORK_ERR);
+ free(temporaryFileName);
+ return;
+ }
+
+ m_path.adoptNS([[NSString stringWithUTF8String:temporaryFileName] retain]);
+ free(temporaryFileName);
+ }
+
+ int dataLength = [data length];
+ if (!dataLength)
+ return;
+
+ int byteCount = write(m_fileDescriptor, [data bytes], dataLength);
+ if (byteCount != dataLength) {
+ // This happens only rarely, when we are out of disk space or have a disk I/O error.
+ LOG_ERROR("error writing to temporary file, errno %d", errno);
+ close(m_fileDescriptor);
+ m_fileDescriptor = -1;
+
+ // This is not a network error, but the only error codes are "network error" and "user break".
+ destroyStreamWithReason(NPRES_NETWORK_ERR);
+ m_path = 0;
+ }
+}
+
+void WebNetscapePluginStream::didFinishLoading(NetscapePlugInStreamLoader*)
+{
+ if (!m_stream.ndata)
+ return;
+
+ if (m_transferMode == NP_ASFILE || m_transferMode == NP_ASFILEONLY) {
+ // Fake the delivery of an empty data to ensure that the file has been created
+ deliverDataToFile([NSData data]);
+ if (m_fileDescriptor != -1)
+ close(m_fileDescriptor);
+ m_fileDescriptor = -1;
+ }
+
+ destroyStreamWithReason(NPRES_DONE);
+}
+
+void WebNetscapePluginStream::didReceiveData(NetscapePlugInStreamLoader*, const char* bytes, int length)
+{
+ NSData *data = [[NSData alloc] initWithBytesNoCopy:(void*)bytes length:length freeWhenDone:NO];
+
+ ASSERT([data length] > 0);
+
+ if (m_transferMode != NP_ASFILEONLY) {
+ if (!m_deliveryData)
+ m_deliveryData.adoptNS([[NSMutableData alloc] initWithCapacity:[data length]]);
+ [m_deliveryData.get() appendData:data];
+ deliverData();
+ }
+ if (m_transferMode == NP_ASFILE || m_transferMode == NP_ASFILEONLY)
+ deliverDataToFile(data);
+
+ [data release];
+}
+
+static NSString *CarbonPathFromPOSIXPath(NSString *posixPath)
+{
+ // Doesn't add a trailing colon for directories; this is a problem for paths to a volume,
+ // so this function would need to be revised if we ever wanted to call it with that.
+
+ CFURLRef url = (CFURLRef)[NSURL fileURLWithPath:posixPath];
+ if (!url)
+ return nil;
+
+ return WebCFAutorelease(CFURLCopyFileSystemPath(url, kCFURLHFSPathStyle));
+}
+
+#endif