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