Changeset 28006 in webkit


Ignore:
Timestamp:
Nov 24, 2007, 3:48:52 PM (18 years ago)
Author:
mrowe@apple.com
Message:

Fix <rdar://problem/5432686> 333MB RPRVT seems to leak @ www.43folders.com (1hr plug-in stream).
http://bugs.webkit.org/show_bug.cgi?id=13705

Reviewed by Tim Hatcher.

Have NP_ASFILE and NP_ASFILEONLY streams write the data to disk as they receive it rather than
dumping the data to disk in a single go when the stream has completed loading. On a test case
involving a 150MB Flash movie being streamed from a local web server this reduces memory consumption
on page load from around 400MB to 22MB.

The only plugin I have found that uses NP_ASFILE or NP_ASFILEONLY on the Mac is our NetscapeMoviePlugin
example code so the NP_ASFILE portion of this change has not had any testing with a real-world plugin.

Location:
trunk
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/WebCore/ChangeLog

    r28005 r28006  
     12007-11-24  Mark Rowe  <mrowe@apple.com>
     2
     3        Reviewed by Tim Hatcher.
     4
     5        Fix <rdar://problem/5432686> 333MB RPRVT seems to leak @ www.43folders.com (1hr plug-in stream).
     6        http://bugs.webkit.org/show_bug.cgi?id=13705
     7
     8        Don't buffer the entire stream contents in memory in the ResourceLoader.
     9
     10        * loader/mac/NetscapePlugInStreamLoaderMac.mm:
     11        (WebCore::NetscapePlugInStreamLoader::NetscapePlugInStreamLoader):
     12        (WebCore::NetscapePlugInStreamLoader::didFinishLoading):
     13        * loader/mac/WebPlugInStreamLoaderDelegate.h:
     14
    1152007-11-23  Adam Roben  <aroben@apple.com>
    216
  • trunk/WebCore/loader/mac/NetscapePlugInStreamLoaderMac.mm

    r25274 r28006  
    4343    , m_stream(stream)
    4444{
     45    setShouldBufferData(false);
    4546}
    4647
     
    107108
    108109    m_documentLoader->removePlugInStreamLoader(this);
    109     NSData *data = resourceData()->createNSData();
    110     [m_stream.get() finishedLoadingWithData:data];
    111     [data release];
     110    [m_stream.get() finishedLoading];
    112111    ResourceLoader::didFinishLoading();
    113112}
  • trunk/WebCore/loader/mac/WebPlugInStreamLoaderDelegate.h

    r16967 r28006  
    4242
    4343- (void)receivedData:(NSData *)data;
    44 - (void)finishedLoadingWithData:(NSData *)data;
     44- (void)finishedLoading;
    4545
    4646@end
  • trunk/WebKit/mac/ChangeLog

    r27995 r28006  
     12007-11-24  Mark Rowe  <mrowe@apple.com>
     2
     3        Reviewed by Tim Hatcher.
     4
     5        Fix <rdar://problem/5432686> 333MB RPRVT seems to leak @ www.43folders.com (1hr plug-in stream).
     6        http://bugs.webkit.org/show_bug.cgi?id=13705
     7
     8        Have NP_ASFILE and NP_ASFILEONLY streams write the data to disk as they receive it rather than
     9        dumping the data to disk in a single go when the stream has completed loading.  On a test case
     10        involving a 150MB Flash movie being streamed from a local web server this reduces memory consumption
     11        on page load from around 400MB to 22MB.
     12
     13        The only plugin I have found that uses NP_ASFILE or NP_ASFILEONLY on the Mac is our NetscapeMoviePlugin
     14        example code so the NP_ASFILE portion of this change has not had any testing with a real-world plugin.
     15
     16        * Plugins/WebBaseNetscapePluginStream.h:
     17        * Plugins/WebBaseNetscapePluginStream.mm:
     18        (-[WebBaseNetscapePluginStream initWithRequestURL:plugin:notifyData:sendNotification:]):
     19        (-[WebBaseNetscapePluginStream dealloc]):
     20        (-[WebBaseNetscapePluginStream finalize]):
     21        (-[WebBaseNetscapePluginStream startStreamResponseURL:expectedContentLength:lastModifiedDate:MIMEType:headers:]):
     22        (-[WebBaseNetscapePluginStream _destroyStream]): Update to work with paths as NSStrings.
     23        (-[WebBaseNetscapePluginStream _deliverDataToFile:]): Open the file if it is not already open, and write any data
     24        to disk.
     25        (-[WebBaseNetscapePluginStream finishedLoading]): If the stream is NP_ASFILE or NP_ASFILEONLY we need to ensure
     26        that the file exists before _destroyStream passes it to the plugin.  Simulating the arrival of an empty data block
     27        ensure that the file will be created if it has not already.
     28        (-[WebBaseNetscapePluginStream receivedData:]):
     29        (CarbonPathFromPOSIXPath):
     30        * Plugins/WebBaseNetscapePluginView.mm:
     31        (-[WebBaseNetscapePluginView pluginViewFinishedLoading:]): Data is dealt with incrementally so there's no need to pass
     32        it to finishedLoading.
     33        (-[WebBaseNetscapePluginView evaluateJavaScriptPluginRequest:]): Ditto.
     34
    1352007-11-23  Oliver Hunt  <oliver@apple.com>
    236
  • trunk/WebKit/mac/Plugins/WebBaseNetscapePluginStream.h

    r24947 r28006  
    4646    int32 offset;
    4747    NPStream stream;
    48     char *path;
     48    NSString *path;
     49    int fileDescriptor;
    4950    BOOL sendNotification;
    5051    void *notifyData;
  • trunk/WebKit/mac/Plugins/WebBaseNetscapePluginStream.mm

    r25024 r28006  
    4343#define WEB_REASON_NONE -1
    4444
    45 static char *CarbonPathFromPOSIXPath(const char *posixPath);
     45static NSString *CarbonPathFromPOSIXPath(NSString *posixPath);
    4646
    4747typedef HashMap<NPStream*, NPP> StreamMap;
     
    118118    notifyData = theNotifyData;
    119119    sendNotification = flag;
     120    fileDescriptor = -1;
    120121
    121122    streams().add(&stream, thePlugin);
     
    134135    // The stream file should have been deleted, and the path freed, in -_destroyStream
    135136    ASSERT(!path);
     137    ASSERT(fileDescriptor == -1);
    136138
    137139    [requestURL release];
     
    158160    // The stream file should have been deleted, and the path freed, in -_destroyStream
    159161    ASSERT(!path);
     162    ASSERT(fileDescriptor == -1);
    160163
    161164    free((void *)stream.url);
     
    258261    offset = 0;
    259262    reason = WEB_REASON_NONE;
     263    // FIXME: If WebNetscapePluginStream called our initializer we wouldn't have to do this here.
     264    fileDescriptor = -1;
    260265
    261266    // FIXME: Need a way to check if stream is seekable
     
    365370    if (stream.ndata != nil) {
    366371        if (reason == NPRES_DONE && (transferMode == NP_ASFILE || transferMode == NP_ASFILEONLY)) {
     372            ASSERT(fileDescriptor == -1);
    367373            ASSERT(path != NULL);
    368             char *carbonPath = CarbonPathFromPOSIXPath(path);
     374            NSString *carbonPath = CarbonPathFromPOSIXPath(path);
    369375            ASSERT(carbonPath != NULL);
    370376            WebBaseNetscapePluginView *pv = pluginView;
    371377            [pv willCallPlugInFunction];
    372             NPP_StreamAsFile(plugin, &stream, carbonPath);
     378            NPP_StreamAsFile(plugin, &stream, [carbonPath fileSystemRepresentation]);
    373379            [pv didCallPlugInFunction];
    374380
     
    377383            // (the stream destruction function), so there can be no expectation that a plugin will read the stream
    378384            // file asynchronously after NPP_StreamAsFile() is called.
    379             unlink(path);
    380             free(path);
    381             path = NULL;
     385            unlink([path fileSystemRepresentation]);
     386            [path release];
     387            path = nil;
    382388            LOG(Plugins, "NPP_StreamAsFile responseURL=%@ path=%s", responseURL, carbonPath);
    383             free(carbonPath);
    384389
    385390            if (isTerminated)
     
    453458    [self setPlugin:NULL];
    454459    [self release];
    455 }
    456 
    457 - (void)finishedLoadingWithData:(NSData *)data
    458 {
    459     if (!stream.ndata)
    460         return;
    461    
    462     if ((transferMode == NP_ASFILE || transferMode == NP_ASFILEONLY) && !path) {
    463         path = strdup("/tmp/WebKitPlugInStreamXXXXXX");
    464         int fd = mkstemp(path);
    465         if (fd == -1) {
    466             // This should almost never happen.
    467             LOG_ERROR("can't make temporary file, almost certainly a problem with /tmp");
    468             // This is not a network error, but the only error codes are "network error" and "user break".
    469             [self _destroyStreamWithReason:NPRES_NETWORK_ERR];
    470             free(path);
    471             path = NULL;
    472             return;
    473         }
    474         int dataLength = [data length];
    475         if (dataLength > 0) {
    476             int byteCount = write(fd, [data bytes], dataLength);
    477             if (byteCount != dataLength) {
    478                 // This happens only rarely, when we are out of disk space or have a disk I/O error.
    479                 LOG_ERROR("error writing to temporary file, errno %d", errno);
    480                 close(fd);
    481                 // This is not a network error, but the only error codes are "network error" and "user break".
    482                 [self _destroyStreamWithReason:NPRES_NETWORK_ERR];
    483                 free(path);
    484                 path = NULL;
    485                 return;
    486             }
    487         }
    488         close(fd);
    489     }
    490 
    491     [self _destroyStreamWithReason:NPRES_DONE];
    492460}
    493461
     
    553521}
    554522
     523- (void)_deliverDataToFile:(NSData *)data
     524{
     525    if (fileDescriptor == -1 && !path) {
     526        NSString *temporaryFileMask = [NSTemporaryDirectory() stringByAppendingPathComponent:@"WebKitPlugInStreamXXXXXX"];
     527        char *temporaryFileName = strdup([temporaryFileMask fileSystemRepresentation]);
     528        fileDescriptor = mkstemp(temporaryFileName);
     529        if (fileDescriptor == -1) {
     530            LOG_ERROR("Can't create a temporary file.");
     531            // This is not a network error, but the only error codes are "network error" and "user break".
     532            [self _destroyStreamWithReason:NPRES_NETWORK_ERR];
     533            free(temporaryFileName);
     534            return;
     535        }
     536
     537        path = [[NSString stringWithUTF8String:temporaryFileName] retain];
     538        free(temporaryFileName);
     539    }
     540
     541    int dataLength = [data length];
     542    if (!dataLength)
     543        return;
     544
     545    int byteCount = write(fileDescriptor, [data bytes], dataLength);
     546    if (byteCount != dataLength) {
     547        // This happens only rarely, when we are out of disk space or have a disk I/O error.
     548        LOG_ERROR("error writing to temporary file, errno %d", errno);
     549        close(fileDescriptor);
     550        fileDescriptor = -1;
     551
     552        // This is not a network error, but the only error codes are "network error" and "user break".
     553        [self _destroyStreamWithReason:NPRES_NETWORK_ERR];
     554        [path release];
     555        path = nil;
     556    }
     557}
     558
     559- (void)finishedLoading
     560{
     561    if (!stream.ndata)
     562        return;
     563
     564    if (transferMode == NP_ASFILE || transferMode == NP_ASFILEONLY) {
     565        // Fake the delivery of an empty data to ensure that the file has been created
     566        [self _deliverDataToFile:[NSData data]];
     567        if (fileDescriptor != -1)
     568            close(fileDescriptor);
     569        fileDescriptor = -1;
     570    }
     571
     572    [self _destroyStreamWithReason:NPRES_DONE];
     573}
     574
    555575- (void)receivedData:(NSData *)data
    556576{
     
    564584        [self _deliverData];
    565585    }
     586    if (transferMode == NP_ASFILE || transferMode == NP_ASFILEONLY)
     587        [self _deliverDataToFile:data];
     588
    566589}
    567590
    568591@end
    569592
    570 static char *CarbonPathFromPOSIXPath(const char *posixPath)
     593static NSString *CarbonPathFromPOSIXPath(NSString *posixPath)
    571594{
    572595    // Doesn't add a trailing colon for directories; this is a problem for paths to a volume,
    573596    // so this function would need to be revised if we ever wanted to call it with that.
    574597
    575     CFURLRef url = CFURLCreateFromFileSystemRepresentation(NULL, (const UInt8 *)posixPath, strlen(posixPath), false);
    576     if (url) {
    577         CFStringRef hfsPath = CFURLCopyFileSystemPath(url, kCFURLHFSPathStyle);
    578         CFRelease(url);
    579         if (hfsPath) {
    580             CFIndex bufSize = CFStringGetMaximumSizeOfFileSystemRepresentation(hfsPath);
    581             char* filename = static_cast<char*>(malloc(bufSize));
    582             CFStringGetFileSystemRepresentation(hfsPath, filename, bufSize);
    583             CFRelease(hfsPath);
    584             return filename;
    585         }
    586     }
    587 
    588     return NULL;
     598    CFURLRef url = (CFURLRef)[NSURL fileURLWithPath:posixPath];
     599    if (!url)
     600        return nil;
     601
     602    NSString *hfsPath = NSMakeCollectable(CFURLCopyFileSystemPath(url, kCFURLHFSPathStyle));
     603    return [hfsPath autorelease];
    589604}
    590605
  • trunk/WebKit/mac/Plugins/WebBaseNetscapePluginView.mm

    r27745 r28006  
    20102010   
    20112011    if ([self isStarted])
    2012         [_manualStream finishedLoadingWithData:[[self dataSource] data]];   
     2012        [_manualStream finishedLoading];
    20132013}
    20142014
     
    20822082                               headers:nil];
    20832083        [stream receivedData:JSData];
    2084         [stream finishedLoadingWithData:JSData];
     2084        [stream finishedLoading];
    20852085        [stream release];
    20862086    }
Note: See TracChangeset for help on using the changeset viewer.