WebNetscapePluginPackage.mm   [plain text]


/*
 * 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 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 "WebNetscapePluginPackage.h"

#import "WebTypesInternal.h"
#import "WebKitLogging.h"
#import "WebKitNSStringExtras.h"
#import "WebNSFileManagerExtras.h"
#import "WebNSObjectExtras.h"
#import <WebCore/npruntime_impl.h>
#import <wtf/RetainPtr.h>

#if USE(PLUGIN_HOST_PROCESS)
#import "NetscapePluginHostManager.h"

using namespace WebKit;
#endif

using namespace WebCore;

@interface WebNetscapePluginPackage (Internal)
- (void)_unloadWithShutdown:(BOOL)shutdown;
@end

@implementation WebNetscapePluginPackage

- (ResFileRefNum)openResourceFile
{
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
    return CFBundleOpenBundleResourceMap(cfBundle.get());
ALLOW_DEPRECATED_DECLARATIONS_END
}

- (void)closeResourceFile:(ResFileRefNum)resRef
{
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
    CFBundleCloseBundleResourceMap(cfBundle.get(), resRef);
ALLOW_DEPRECATED_DECLARATIONS_END
}

- (BOOL)_initWithPath:(NSString *)pluginPath
{
    resourceRef = -1;
    
    OSType type = 0;

    if (!cfBundle)
        return NO;

    CFBundleGetPackageInfo(cfBundle.get(), &type, NULL);
    
    if (type != FOUR_CHAR_CODE('BRPL'))
        return NO;

#if USE(PLUGIN_HOST_PROCESS)
    auto archs = adoptCF(CFBundleCopyExecutableArchitectures(cfBundle.get()));
    if ([(__bridge NSArray *)archs.get() containsObject:@(NSBundleExecutableArchitectureX86_64)])
        pluginHostArchitecture = CPU_TYPE_X86_64;
    else if ([(__bridge NSArray *)archs.get() containsObject:@(NSBundleExecutableArchitectureI386)])
        pluginHostArchitecture = CPU_TYPE_X86;
    else
        return NO;
#else
    RetainPtr<CFURLRef> executableURL = adoptCF(CFBundleCopyExecutableURL(cfBundle.get()));
    if (!executableURL)
        return NO;
    NSFileHandle *executableFile = [NSFileHandle fileHandleForReadingAtPath:[(NSURL *)executableURL.get() path]];
    NSData *data = [executableFile readDataOfLength:512];
    [executableFile closeFile];

     if (![self isNativeLibraryData:data])
         return NO;
#endif

    if (![self getPluginInfoFromPLists])
        return NO;
    
    return YES;
}

- (id)initWithPath:(NSString *)pluginPath
{
    if (!(self = [super initWithPath:pluginPath]))
        return nil;
    
    // Initializing a plugin package can cause it to be loaded.  If there was an error initializing the plugin package,
    // ensure that it is unloaded before deallocating it (WebBasePluginPackage requires & asserts this).
    if (![self _initWithPath:pluginPath]) {
        [self _unloadWithShutdown:YES];
        [self release];
        return nil;
    }
        
    return self;
}

#if USE(PLUGIN_HOST_PROCESS)

- (cpu_type_t)pluginHostArchitecture
{
    return pluginHostArchitecture;
}

- (void)createPropertyListFile
{
    NetscapePluginHostManager::singleton().createPropertyListFile(path, pluginHostArchitecture, [self bundleIdentifier]);
}

#endif

- (void)unload
{
    [self _unloadWithShutdown:YES];
}

- (BOOL)_tryLoad
{
    NP_GetEntryPointsFuncPtr NP_GetEntryPoints = NULL;
    NP_InitializeFuncPtr NP_Initialize = NULL;
    NPError npErr;

#if !LOG_DISABLED
    CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
    CFAbsoluteTime currentTime;
    CFAbsoluteTime duration;
#endif
    LOG(Plugins, "%f Load timing started for: %@", start, (NSString *)[self pluginInfo].name);

    if (isLoaded)
        return YES;
    
    if (!CFBundleLoadExecutable(cfBundle.get()))
        return NO;
#if !LOG_DISABLED
    currentTime = CFAbsoluteTimeGetCurrent();
    duration = currentTime - start;
#endif
    LOG(Plugins, "%f CFBundleLoadExecutable took %f seconds", currentTime, duration);
    isLoaded = YES;

    NP_Initialize = (NP_InitializeFuncPtr)CFBundleGetFunctionPointerForName(cfBundle.get(), CFSTR("NP_Initialize"));
    NP_GetEntryPoints = (NP_GetEntryPointsFuncPtr)CFBundleGetFunctionPointerForName(cfBundle.get(), CFSTR("NP_GetEntryPoints"));
    NP_Shutdown = (NPP_ShutdownProcPtr)CFBundleGetFunctionPointerForName(cfBundle.get(), CFSTR("NP_Shutdown"));
    if (!NP_Initialize || !NP_GetEntryPoints || !NP_Shutdown)
        return NO;

    ALLOW_DEPRECATED_DECLARATIONS_BEGIN
    // Plugins (at least QT) require that you call UseResFile on the resource file before loading it.
    resourceRef = [self openResourceFile];
    if (resourceRef != -1) {
        UseResFile(resourceRef);
    }
    ALLOW_DEPRECATED_DECLARATIONS_END

    browserFuncs.version = NP_VERSION_MINOR;
    browserFuncs.size = sizeof(NPNetscapeFuncs);
    browserFuncs.geturl = NPN_GetURL;
    browserFuncs.posturl = NPN_PostURL;
    browserFuncs.requestread = NPN_RequestRead;
    browserFuncs.newstream = NPN_NewStream;
    browserFuncs.write = NPN_Write;
    browserFuncs.destroystream = NPN_DestroyStream;
    browserFuncs.status = NPN_Status;
    browserFuncs.uagent = NPN_UserAgent;
    browserFuncs.memalloc = NPN_MemAlloc;
    browserFuncs.memfree = NPN_MemFree;
    browserFuncs.memflush = NPN_MemFlush;
    browserFuncs.reloadplugins = NPN_ReloadPlugins;
    browserFuncs.geturlnotify = NPN_GetURLNotify;
    browserFuncs.posturlnotify = NPN_PostURLNotify;
    browserFuncs.getvalue = NPN_GetValue;
    browserFuncs.setvalue = NPN_SetValue;
    browserFuncs.invalidaterect = NPN_InvalidateRect;
    browserFuncs.invalidateregion = NPN_InvalidateRegion;
    browserFuncs.forceredraw = NPN_ForceRedraw;
    browserFuncs.getJavaEnv = NPN_GetJavaEnv;
    browserFuncs.getJavaPeer = NPN_GetJavaPeer;
    browserFuncs.pushpopupsenabledstate = NPN_PushPopupsEnabledState;
    browserFuncs.poppopupsenabledstate = NPN_PopPopupsEnabledState;
    browserFuncs.pluginthreadasynccall = NPN_PluginThreadAsyncCall;
    browserFuncs.getvalueforurl = NPN_GetValueForURL;
    browserFuncs.setvalueforurl = NPN_SetValueForURL;
    browserFuncs.getauthenticationinfo = NPN_GetAuthenticationInfo;
    browserFuncs.scheduletimer = NPN_ScheduleTimer;
    browserFuncs.unscheduletimer = NPN_UnscheduleTimer;
    browserFuncs.popupcontextmenu = NPN_PopUpContextMenu;
    browserFuncs.convertpoint = NPN_ConvertPoint;

    browserFuncs.releasevariantvalue = _NPN_ReleaseVariantValue;
    browserFuncs.getstringidentifier = _NPN_GetStringIdentifier;
    browserFuncs.getstringidentifiers = _NPN_GetStringIdentifiers;
    browserFuncs.getintidentifier = _NPN_GetIntIdentifier;
    browserFuncs.identifierisstring = _NPN_IdentifierIsString;
    browserFuncs.utf8fromidentifier = _NPN_UTF8FromIdentifier;
    browserFuncs.intfromidentifier = _NPN_IntFromIdentifier;
    browserFuncs.createobject = _NPN_CreateObject;
    browserFuncs.retainobject = _NPN_RetainObject;
    browserFuncs.releaseobject = _NPN_ReleaseObject;
    browserFuncs.hasmethod = _NPN_HasMethod;
    browserFuncs.invoke = _NPN_Invoke;
    browserFuncs.invokeDefault = _NPN_InvokeDefault;
    browserFuncs.evaluate = _NPN_Evaluate;
    browserFuncs.hasproperty = _NPN_HasProperty;
    browserFuncs.getproperty = _NPN_GetProperty;
    browserFuncs.setproperty = _NPN_SetProperty;
    browserFuncs.removeproperty = _NPN_RemoveProperty;
    browserFuncs.setexception = _NPN_SetException;
    browserFuncs.enumerate = _NPN_Enumerate;
    browserFuncs.construct = _NPN_Construct;

#if !LOG_DISABLED
    CFAbsoluteTime initializeStart = CFAbsoluteTimeGetCurrent();
#endif
    LOG(Plugins, "%f NP_Initialize timing started", initializeStart);
    npErr = NP_Initialize(&browserFuncs);
    if (npErr != NPERR_NO_ERROR)
        return NO;
#if !LOG_DISABLED
    currentTime = CFAbsoluteTimeGetCurrent();
    duration = currentTime - initializeStart;
#endif
    LOG(Plugins, "%f NP_Initialize took %f seconds", currentTime, duration);

    pluginFuncs.size = sizeof(NPPluginFuncs);

    npErr = NP_GetEntryPoints(&pluginFuncs);
    if (npErr != NPERR_NO_ERROR)
        return NO;

    pluginSize = pluginFuncs.size;
    pluginVersion = pluginFuncs.version;

#if !LOG_DISABLED
    currentTime = CFAbsoluteTimeGetCurrent();
    duration = currentTime - start;
#endif
    LOG(Plugins, "%f Total load time: %f seconds", currentTime, duration);

    return YES;
}

- (BOOL)load
{    
    if ([self _tryLoad])
        return [super load];

    [self _unloadWithShutdown:NO];
    return NO;
}

- (NPPluginFuncs *)pluginFuncs
{
    return &pluginFuncs;
}

- (NPNetscapeFuncs *)browserFuncs
{
    return &browserFuncs;
}

- (void)wasRemovedFromPluginDatabase:(WebPluginDatabase *)database
{
    [super wasRemovedFromPluginDatabase:database];
    
    // Unload when removed from final plug-in database
    if ([pluginDatabases count] == 0)
        [self _unloadWithShutdown:YES];
}

- (void)open
{
    instanceCount++;
    
    // Handle the case where all instances close a plug-in package, but another
    // instance opens the package before it is unloaded (which only happens when
    // the plug-in database is refreshed)
    needsUnload = NO;
    
    if (!isLoaded) {
        // Should load when the first instance opens the plug-in package
        ASSERT(instanceCount == 1);
        [self load];
    }
}

- (void)close
{
    ASSERT(instanceCount > 0);
    instanceCount--;
    if (instanceCount == 0 && needsUnload)
        [self _unloadWithShutdown:YES];
}


- (BOOL)supportsSnapshotting
{
    if ([self bundleIdentifier] != "com.macromedia.Flash Player.plugin")
        return YES;
    
    // Flash has a bogus Info.plist entry for CFBundleVersionString, so use CFBundleShortVersionString.
    NSString *versionString = (NSString *)CFDictionaryGetValue(CFBundleGetInfoDictionary(cfBundle.get()), CFSTR("CFBundleShortVersionString"));
    
    if (![versionString hasPrefix:@"10.1"])
        return YES;
    
    // Some prerelease versions of Flash 10.1 crash when sent a drawRect event using the CA drawing model: <rdar://problem/7739922>
    return CFStringCompare((CFStringRef)versionString, CFSTR("10.1.53.60"), kCFCompareNumerically) != kCFCompareLessThan;
}

@end

@implementation WebNetscapePluginPackage (Internal)

- (void)_unloadWithShutdown:(BOOL)shutdown
{
    if (!isLoaded)
        return;
    
    LOG(Plugins, "Unloading %@...", (NSString *)pluginInfo.name);

    // Cannot unload a plug-in package while an instance is still using it
    if (instanceCount > 0) {
        needsUnload = YES;
        return;
    }

    if (shutdown && NP_Shutdown)
        NP_Shutdown();

    if (resourceRef != -1)
        [self closeResourceFile:resourceRef];

    LOG(Plugins, "Plugin Unloaded");
    isLoaded = NO;
}

@end
#endif