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