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 */ 28 29#if ENABLE(NETSCAPE_PLUGIN_API) 30#import "WebBaseNetscapePluginStream.h" 31 32#import "WebNetscapePluginView.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 <Foundation/NSURLResponse.h> 41#import <runtime/JSLock.h> 42#import <WebCore/DocumentLoader.h> 43#import <WebCore/Frame.h> 44#import <WebCore/FrameLoader.h> 45#import <WebCore/WebCoreObjCExtras.h> 46#import <WebCore/WebCoreURLResponse.h> 47#import <WebKitSystemInterface.h> 48#import <wtf/HashMap.h> 49#import <wtf/StdLibExtras.h> 50 51using namespace WebCore; 52 53#define WEB_REASON_NONE -1 54 55static NSString *CarbonPathFromPOSIXPath(NSString *posixPath); 56 57class PluginStopDeferrer { 58public: 59 PluginStopDeferrer(WebNetscapePluginView* pluginView) 60 : m_pluginView(pluginView) 61 { 62 ASSERT(m_pluginView); 63 64 [m_pluginView.get() willCallPlugInFunction]; 65 } 66 67 ~PluginStopDeferrer() 68 { 69 ASSERT(m_pluginView); 70 [m_pluginView.get() didCallPlugInFunction]; 71 } 72 73private: 74 RetainPtr<WebNetscapePluginView> m_pluginView; 75}; 76 77typedef HashMap<NPStream*, NPP> StreamMap; 78static StreamMap& streams() 79{ 80 DEFINE_STATIC_LOCAL(StreamMap, staticStreams, ()); 81 return staticStreams; 82} 83 84NPP WebNetscapePluginStream::ownerForStream(NPStream *stream) 85{ 86 return streams().get(stream); 87} 88 89NPReason WebNetscapePluginStream::reasonForError(NSError *error) 90{ 91 if (!error) 92 return NPRES_DONE; 93 94 if ([[error domain] isEqualToString:NSURLErrorDomain] && [error code] == NSURLErrorCancelled) 95 return NPRES_USER_BREAK; 96 97 return NPRES_NETWORK_ERR; 98} 99 100NSError *WebNetscapePluginStream::pluginCancelledConnectionError() const 101{ 102 return [[[NSError alloc] _initWithPluginErrorCode:WebKitErrorPlugInCancelledConnection 103 contentURL:m_responseURL ? m_responseURL.get() : m_requestURL.get() 104 pluginPageURL:nil 105 pluginName:[[m_pluginView.get() pluginPackage] name] 106 MIMEType:m_mimeType.get()] autorelease]; 107} 108 109NSError *WebNetscapePluginStream::errorForReason(NPReason reason) const 110{ 111 if (reason == NPRES_DONE) 112 return nil; 113 114 if (reason == NPRES_USER_BREAK) 115 return [NSError _webKitErrorWithDomain:NSURLErrorDomain 116 code:NSURLErrorCancelled 117 URL:m_responseURL ? m_responseURL.get() : m_requestURL.get()]; 118 119 return pluginCancelledConnectionError(); 120} 121 122WebNetscapePluginStream::WebNetscapePluginStream(FrameLoader* frameLoader) 123 : m_plugin(0) 124 , m_transferMode(0) 125 , m_offset(0) 126 , m_fileDescriptor(-1) 127 , m_sendNotification(false) 128 , m_notifyData(0) 129 , m_headers(0) 130 , m_reason(NPRES_BASE) 131 , m_isTerminated(false) 132 , m_newStreamSuccessful(false) 133 , m_frameLoader(frameLoader) 134 , m_pluginFuncs(0) 135 , m_deliverDataTimer(this, &WebNetscapePluginStream::deliverDataTimerFired) 136{ 137 memset(&m_stream, 0, sizeof(NPStream)); 138} 139 140WebNetscapePluginStream::WebNetscapePluginStream(NSURLRequest *request, NPP plugin, bool sendNotification, void* notifyData) 141 : m_requestURL([request URL]) 142 , m_plugin(0) 143 , m_transferMode(0) 144 , m_offset(0) 145 , m_fileDescriptor(-1) 146 , m_sendNotification(sendNotification) 147 , m_notifyData(notifyData) 148 , m_headers(0) 149 , m_reason(NPRES_BASE) 150 , m_isTerminated(false) 151 , m_newStreamSuccessful(false) 152 , m_frameLoader(0) 153 , m_request(AdoptNS, [request mutableCopy]) 154 , m_pluginFuncs(0) 155 , m_deliverDataTimer(this, &WebNetscapePluginStream::deliverDataTimerFired) 156{ 157 memset(&m_stream, 0, sizeof(NPStream)); 158 159 WebNetscapePluginView *view = (WebNetscapePluginView *)plugin->ndata; 160 161 // This check has already been done by the plug-in view. 162 ASSERT(FrameLoader::canLoad([request URL], String(), core([view webFrame])->document())); 163 164 ASSERT([request URL]); 165 ASSERT(plugin); 166 167 setPlugin(plugin); 168 169 streams().add(&m_stream, plugin); 170 171 if (core([view webFrame])->loader()->shouldHideReferrer([request URL], core([view webFrame])->loader()->outgoingReferrer())) 172 [m_request.get() _web_setHTTPReferrer:nil]; 173} 174 175WebNetscapePluginStream::~WebNetscapePluginStream() 176{ 177 ASSERT(!m_plugin); 178 ASSERT(m_isTerminated); 179 ASSERT(!m_stream.ndata); 180 181 // The stream file should have been deleted, and the path freed, in -_destroyStream 182 ASSERT(!m_path); 183 ASSERT(m_fileDescriptor == -1); 184 185 free((void *)m_stream.url); 186 free(m_headers); 187 188 streams().remove(&m_stream); 189} 190 191void WebNetscapePluginStream::setPlugin(NPP plugin) 192{ 193 if (plugin) { 194 m_plugin = plugin; 195 m_pluginView = static_cast<WebNetscapePluginView *>(m_plugin->ndata); 196 197 WebNetscapePluginPackage *pluginPackage = [m_pluginView.get() pluginPackage]; 198 199 m_pluginFuncs = [pluginPackage pluginFuncs]; 200 } else { 201 WebNetscapePluginView *view = m_pluginView.get(); 202 m_plugin = 0; 203 m_pluginFuncs = 0; 204 205 [view disconnectStream:this]; 206 m_pluginView = 0; 207 } 208} 209 210void WebNetscapePluginStream::startStream(NSURL *url, long long expectedContentLength, NSDate *lastModifiedDate, NSString *mimeType, NSData *headers) 211{ 212 ASSERT(!m_isTerminated); 213 214 m_responseURL = url; 215 m_mimeType = mimeType; 216 217 free((void *)m_stream.url); 218 m_stream.url = strdup([m_responseURL.get() _web_URLCString]); 219 220 m_stream.ndata = this; 221 m_stream.end = expectedContentLength > 0 ? (uint32)expectedContentLength : 0; 222 m_stream.lastmodified = (uint32)[lastModifiedDate timeIntervalSince1970]; 223 m_stream.notifyData = m_notifyData; 224 225 if (headers) { 226 unsigned len = [headers length]; 227 m_headers = (char*) malloc(len + 1); 228 [headers getBytes:m_headers]; 229 m_headers[len] = 0; 230 m_stream.headers = m_headers; 231 } 232 233 m_transferMode = NP_NORMAL; 234 m_offset = 0; 235 m_reason = WEB_REASON_NONE; 236 // FIXME: If WebNetscapePluginStream called our initializer we wouldn't have to do this here. 237 m_fileDescriptor = -1; 238 239 // FIXME: Need a way to check if stream is seekable 240 241 NPError npErr; 242 { 243 PluginStopDeferrer deferrer(m_pluginView.get()); 244 npErr = m_pluginFuncs->newstream(m_plugin, (char *)[m_mimeType.get() UTF8String], &m_stream, NO, &m_transferMode); 245 } 246 247 LOG(Plugins, "NPP_NewStream URL=%@ MIME=%@ error=%d", m_responseURL.get(), m_mimeType.get(), npErr); 248 249 if (npErr != NPERR_NO_ERROR) { 250 LOG_ERROR("NPP_NewStream failed with error: %d responseURL: %@", npErr, m_responseURL.get()); 251 // Calling cancelLoadWithError: cancels the load, but doesn't call NPP_DestroyStream. 252 cancelLoadWithError(pluginCancelledConnectionError()); 253 return; 254 } 255 256 m_newStreamSuccessful = true; 257 258 switch (m_transferMode) { 259 case NP_NORMAL: 260 LOG(Plugins, "Stream type: NP_NORMAL"); 261 break; 262 case NP_ASFILEONLY: 263 LOG(Plugins, "Stream type: NP_ASFILEONLY"); 264 break; 265 case NP_ASFILE: 266 LOG(Plugins, "Stream type: NP_ASFILE"); 267 break; 268 case NP_SEEK: 269 LOG_ERROR("Stream type: NP_SEEK not yet supported"); 270 cancelLoadAndDestroyStreamWithError(pluginCancelledConnectionError()); 271 break; 272 default: 273 LOG_ERROR("unknown stream type"); 274 } 275} 276 277void WebNetscapePluginStream::start() 278{ 279 ASSERT(m_request); 280 ASSERT(!m_frameLoader); 281 ASSERT(!m_loader); 282 283 m_loader = NetscapePlugInStreamLoader::create(core([m_pluginView.get() webFrame]), this); 284 m_loader->setShouldBufferData(false); 285 286 m_loader->documentLoader()->addPlugInStreamLoader(m_loader.get()); 287 m_loader->load(m_request.get()); 288} 289 290void WebNetscapePluginStream::stop() 291{ 292 ASSERT(!m_frameLoader); 293 294 if (!m_loader->isDone()) 295 cancelLoadAndDestroyStreamWithError(m_loader->cancelledError()); 296} 297 298void WebNetscapePluginStream::didReceiveResponse(NetscapePlugInStreamLoader*, const ResourceResponse& response) 299{ 300 NSURLResponse *r = response.nsURLResponse(); 301 302 NSMutableData *theHeaders = nil; 303 long long expectedContentLength = [r expectedContentLength]; 304 305 if ([r isKindOfClass:[NSHTTPURLResponse class]]) { 306 NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)r; 307 theHeaders = [NSMutableData dataWithCapacity:1024]; 308 309 // FIXME: it would be nice to be able to get the raw HTTP header block. 310 // This includes the HTTP version, the real status text, 311 // all headers in their original order and including duplicates, 312 // and all original bytes verbatim, rather than sent through Unicode translation. 313 // Unfortunately NSHTTPURLResponse doesn't provide access at that low a level. 314 315 [theHeaders appendBytes:"HTTP " length:5]; 316 char statusStr[10]; 317 long statusCode = [httpResponse statusCode]; 318 snprintf(statusStr, sizeof(statusStr), "%ld", statusCode); 319 [theHeaders appendBytes:statusStr length:strlen(statusStr)]; 320 [theHeaders appendBytes:" OK\n" length:4]; 321 322 // HACK: pass the headers through as UTF-8. 323 // This is not the intended behavior; we're supposed to pass original bytes verbatim. 324 // But we don't have the original bytes, we have NSStrings built by the URL loading system. 325 // It hopefully shouldn't matter, since RFC2616/RFC822 require ASCII-only headers, 326 // but surely someone out there is using non-ASCII characters, and hopefully UTF-8 is adequate here. 327 // It seems better than NSASCIIStringEncoding, which will lose information if non-ASCII is used. 328 329 NSDictionary *headerDict = [httpResponse allHeaderFields]; 330 NSArray *keys = [[headerDict allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]; 331 NSEnumerator *i = [keys objectEnumerator]; 332 NSString *k; 333 while ((k = [i nextObject]) != nil) { 334 NSString *v = [headerDict objectForKey:k]; 335 [theHeaders appendData:[k dataUsingEncoding:NSUTF8StringEncoding]]; 336 [theHeaders appendBytes:": " length:2]; 337 [theHeaders appendData:[v dataUsingEncoding:NSUTF8StringEncoding]]; 338 [theHeaders appendBytes:"\n" length:1]; 339 } 340 341 // If the content is encoded (most likely compressed), then don't send its length to the plugin, 342 // which is only interested in the decoded length, not yet known at the moment. 343 // <rdar://problem/4470599> tracks a request for -[NSURLResponse expectedContentLength] to incorporate this logic. 344 NSString *contentEncoding = (NSString *)[[(NSHTTPURLResponse *)r allHeaderFields] objectForKey:@"Content-Encoding"]; 345 if (contentEncoding && ![contentEncoding isEqualToString:@"identity"]) 346 expectedContentLength = -1; 347 348 // startStreamResponseURL:... will null-terminate. 349 } 350 351 startStream([r URL], expectedContentLength, WKGetNSURLResponseLastModifiedDate(r), [r MIMEType], theHeaders); 352} 353 354void WebNetscapePluginStream::startStreamWithResponse(NSURLResponse *response) 355{ 356 didReceiveResponse(0, response); 357} 358 359bool WebNetscapePluginStream::wantsAllStreams() const 360{ 361 if (!m_pluginFuncs->getvalue) 362 return false; 363 364 void *value = 0; 365 NPError error; 366 { 367 PluginStopDeferrer deferrer(m_pluginView.get()); 368 JSC::JSLock::DropAllLocks dropAllLocks(JSC::SilenceAssertionsOnly); 369 error = m_pluginFuncs->getvalue(m_plugin, NPPVpluginWantsAllNetworkStreams, &value); 370 } 371 if (error != NPERR_NO_ERROR) 372 return false; 373 374 return value; 375} 376 377void WebNetscapePluginStream::destroyStream() 378{ 379 if (m_isTerminated) 380 return; 381 382 RefPtr<WebNetscapePluginStream> protect(this); 383 384 ASSERT(m_reason != WEB_REASON_NONE); 385 ASSERT([m_deliveryData.get() length] == 0); 386 387 m_deliverDataTimer.stop(); 388 389 if (m_stream.ndata) { 390 if (m_reason == NPRES_DONE && (m_transferMode == NP_ASFILE || m_transferMode == NP_ASFILEONLY)) { 391 ASSERT(m_fileDescriptor == -1); 392 ASSERT(m_path); 393 NSString *carbonPath = CarbonPathFromPOSIXPath(m_path.get()); 394 ASSERT(carbonPath != NULL); 395 396 PluginStopDeferrer deferrer(m_pluginView.get()); 397 m_pluginFuncs->asfile(m_plugin, &m_stream, [carbonPath fileSystemRepresentation]); 398 LOG(Plugins, "NPP_StreamAsFile responseURL=%@ path=%s", m_responseURL.get(), carbonPath); 399 } 400 401 if (m_path) { 402 // Delete the file after calling NPP_StreamAsFile(), instead of in -dealloc/-finalize. It should be OK 403 // to delete the file here -- NPP_StreamAsFile() is always called immediately before NPP_DestroyStream() 404 // (the stream destruction function), so there can be no expectation that a plugin will read the stream 405 // file asynchronously after NPP_StreamAsFile() is called. 406 unlink([m_path.get() fileSystemRepresentation]); 407 m_path = 0; 408 409 if (m_isTerminated) 410 return; 411 } 412 413 if (m_fileDescriptor != -1) { 414 // The file may still be open if we are destroying the stream before it completed loading. 415 close(m_fileDescriptor); 416 m_fileDescriptor = -1; 417 } 418 419 if (m_newStreamSuccessful) { 420 PluginStopDeferrer deferrer(m_pluginView.get()); 421#if !LOG_DISABLED 422 NPError npErr = 423#endif 424 m_pluginFuncs->destroystream(m_plugin, &m_stream, m_reason); 425 LOG(Plugins, "NPP_DestroyStream responseURL=%@ error=%d", m_responseURL.get(), npErr); 426 } 427 428 free(m_headers); 429 m_headers = NULL; 430 m_stream.headers = NULL; 431 432 m_stream.ndata = 0; 433 434 if (m_isTerminated) 435 return; 436 } 437 438 if (m_sendNotification) { 439 // NPP_URLNotify expects the request URL, not the response URL. 440 PluginStopDeferrer deferrer(m_pluginView.get()); 441 m_pluginFuncs->urlnotify(m_plugin, [m_requestURL.get() _web_URLCString], m_reason, m_notifyData); 442 LOG(Plugins, "NPP_URLNotify requestURL=%@ reason=%d", m_requestURL.get(), m_reason); 443 } 444 445 m_isTerminated = true; 446 447 setPlugin(0); 448} 449 450void WebNetscapePluginStream::destroyStreamWithReason(NPReason reason) 451{ 452 m_reason = reason; 453 if (m_reason != NPRES_DONE) { 454 // Stop any pending data from being streamed. 455 [m_deliveryData.get() setLength:0]; 456 } else if ([m_deliveryData.get() length] > 0) { 457 // There is more data to be streamed, don't destroy the stream now. 458 return; 459 } 460 461 RefPtr<WebNetscapePluginStream> protect(this); 462 destroyStream(); 463 ASSERT(!m_stream.ndata); 464} 465 466void WebNetscapePluginStream::cancelLoadWithError(NSError *error) 467{ 468 if (m_frameLoader) { 469 ASSERT(!m_loader); 470 471 DocumentLoader* documentLoader = m_frameLoader->activeDocumentLoader(); 472 ASSERT(documentLoader); 473 474 if (documentLoader->isLoadingMainResource()) 475 documentLoader->cancelMainResourceLoad(error); 476 return; 477 } 478 479 if (!m_loader->isDone()) 480 m_loader->cancel(error); 481} 482 483void WebNetscapePluginStream::destroyStreamWithError(NSError *error) 484{ 485 destroyStreamWithReason(reasonForError(error)); 486} 487 488void WebNetscapePluginStream::didFail(WebCore::NetscapePlugInStreamLoader*, const WebCore::ResourceError& error) 489{ 490 destroyStreamWithError(error); 491} 492 493void WebNetscapePluginStream::cancelLoadAndDestroyStreamWithError(NSError *error) 494{ 495 RefPtr<WebNetscapePluginStream> protect(this); 496 cancelLoadWithError(error); 497 destroyStreamWithError(error); 498 setPlugin(0); 499} 500 501void WebNetscapePluginStream::deliverData() 502{ 503 if (!m_stream.ndata || [m_deliveryData.get() length] == 0) 504 return; 505 506 RefPtr<WebNetscapePluginStream> protect(this); 507 508 int32 totalBytes = [m_deliveryData.get() length]; 509 int32 totalBytesDelivered = 0; 510 511 while (totalBytesDelivered < totalBytes) { 512 PluginStopDeferrer deferrer(m_pluginView.get()); 513 int32 deliveryBytes = m_pluginFuncs->writeready(m_plugin, &m_stream); 514 LOG(Plugins, "NPP_WriteReady responseURL=%@ bytes=%d", m_responseURL.get(), deliveryBytes); 515 516 if (m_isTerminated) 517 return; 518 519 if (deliveryBytes <= 0) { 520 // Plug-in can't receive anymore data right now. Send it later. 521 if (!m_deliverDataTimer.isActive()) 522 m_deliverDataTimer.startOneShot(0); 523 break; 524 } else { 525 deliveryBytes = MIN(deliveryBytes, totalBytes - totalBytesDelivered); 526 NSData *subdata = [m_deliveryData.get() subdataWithRange:NSMakeRange(totalBytesDelivered, deliveryBytes)]; 527 PluginStopDeferrer deferrer(m_pluginView.get()); 528 deliveryBytes = m_pluginFuncs->write(m_plugin, &m_stream, m_offset, [subdata length], (void *)[subdata bytes]); 529 if (deliveryBytes < 0) { 530 // Netscape documentation says that a negative result from NPP_Write means cancel the load. 531 cancelLoadAndDestroyStreamWithError(pluginCancelledConnectionError()); 532 return; 533 } 534 deliveryBytes = MIN((unsigned)deliveryBytes, [subdata length]); 535 m_offset += deliveryBytes; 536 totalBytesDelivered += deliveryBytes; 537 LOG(Plugins, "NPP_Write responseURL=%@ bytes=%d total-delivered=%d/%d", m_responseURL.get(), deliveryBytes, m_offset, m_stream.end); 538 } 539 } 540 541 if (totalBytesDelivered > 0) { 542 if (totalBytesDelivered < totalBytes) { 543 NSMutableData *newDeliveryData = [[NSMutableData alloc] initWithCapacity:totalBytes - totalBytesDelivered]; 544 [newDeliveryData appendBytes:(char *)[m_deliveryData.get() bytes] + totalBytesDelivered length:totalBytes - totalBytesDelivered]; 545 546 m_deliveryData.adoptNS(newDeliveryData); 547 } else { 548 [m_deliveryData.get() setLength:0]; 549 if (m_reason != WEB_REASON_NONE) 550 destroyStream(); 551 } 552 } 553} 554 555void WebNetscapePluginStream::deliverDataTimerFired(WebCore::Timer<WebNetscapePluginStream>* timer) 556{ 557 deliverData(); 558} 559 560void WebNetscapePluginStream::deliverDataToFile(NSData *data) 561{ 562 if (m_fileDescriptor == -1 && !m_path) { 563 NSString *temporaryFileMask = [NSTemporaryDirectory() stringByAppendingPathComponent:@"WebKitPlugInStreamXXXXXX"]; 564 char *temporaryFileName = strdup([temporaryFileMask fileSystemRepresentation]); 565 m_fileDescriptor = mkstemp(temporaryFileName); 566 if (m_fileDescriptor == -1) { 567 LOG_ERROR("Can't create a temporary file."); 568 // This is not a network error, but the only error codes are "network error" and "user break". 569 destroyStreamWithReason(NPRES_NETWORK_ERR); 570 free(temporaryFileName); 571 return; 572 } 573 574 m_path.adoptNS([[NSString stringWithUTF8String:temporaryFileName] retain]); 575 free(temporaryFileName); 576 } 577 578 int dataLength = [data length]; 579 if (!dataLength) 580 return; 581 582 int byteCount = write(m_fileDescriptor, [data bytes], dataLength); 583 if (byteCount != dataLength) { 584 // This happens only rarely, when we are out of disk space or have a disk I/O error. 585 LOG_ERROR("error writing to temporary file, errno %d", errno); 586 close(m_fileDescriptor); 587 m_fileDescriptor = -1; 588 589 // This is not a network error, but the only error codes are "network error" and "user break". 590 destroyStreamWithReason(NPRES_NETWORK_ERR); 591 m_path = 0; 592 } 593} 594 595void WebNetscapePluginStream::didFinishLoading(NetscapePlugInStreamLoader*) 596{ 597 if (!m_stream.ndata) 598 return; 599 600 if (m_transferMode == NP_ASFILE || m_transferMode == NP_ASFILEONLY) { 601 // Fake the delivery of an empty data to ensure that the file has been created 602 deliverDataToFile([NSData data]); 603 if (m_fileDescriptor != -1) 604 close(m_fileDescriptor); 605 m_fileDescriptor = -1; 606 } 607 608 destroyStreamWithReason(NPRES_DONE); 609} 610 611void WebNetscapePluginStream::didReceiveData(NetscapePlugInStreamLoader*, const char* bytes, int length) 612{ 613 NSData *data = [[NSData alloc] initWithBytesNoCopy:(void*)bytes length:length freeWhenDone:NO]; 614 615 ASSERT([data length] > 0); 616 617 if (m_transferMode != NP_ASFILEONLY) { 618 if (!m_deliveryData) 619 m_deliveryData.adoptNS([[NSMutableData alloc] initWithCapacity:[data length]]); 620 [m_deliveryData.get() appendData:data]; 621 deliverData(); 622 } 623 if (m_transferMode == NP_ASFILE || m_transferMode == NP_ASFILEONLY) 624 deliverDataToFile(data); 625 626 [data release]; 627} 628 629static NSString *CarbonPathFromPOSIXPath(NSString *posixPath) 630{ 631 // Doesn't add a trailing colon for directories; this is a problem for paths to a volume, 632 // so this function would need to be revised if we ever wanted to call it with that. 633 634 CFURLRef url = (CFURLRef)[NSURL fileURLWithPath:posixPath]; 635 if (!url) 636 return nil; 637 638 return WebCFAutorelease(CFURLCopyFileSystemPath(url, kCFURLHFSPathStyle)); 639} 640 641#endif 642