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