blob: b8dca9af10232264a99aef5610940c475de7b725 [file] [log] [blame]
#ifdef ANDROID_PLUGINS
/*
* Copyright (C) 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``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 COMPUTER, INC. OR
* 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.
*/
#include "config.h"
#include "WebCoreJni.h"
#include "PluginPackageAndroid.h"
#include "PluginDatabaseAndroid.h"
#include "Timer.h"
#include "DeprecatedString.h"
#include "PlatformString.h"
#include "CString.h"
#include "npruntime_impl.h"
#include <dlfcn.h>
#include <errno.h>
#include "PluginDebug.h"
namespace WebCore {
PluginPackageAndroid::PluginPackageAndroid(const String& path,
uint32 lastModified)
: m_path(path),
m_name(),
m_fileName(),
m_description(),
m_lastModified(lastModified),
m_pluginFuncs(),
m_mimeToDescriptions(),
m_mimeToExtensions(),
m_env(0),
m_pluginObject(0),
m_libraryHandle(NULL),
m_libraryInitialized(false),
m_NP_Initialize((NP_InitializeFuncPtr) NULL),
m_NP_Shutdown((NP_ShutdownFuncPtr) NULL),
m_NP_GetMIMEDescription((NP_GetMIMEDescriptionFuncPtr) NULL),
m_NP_GetValue((NP_GetValueFuncPtr) NULL)
{
PLUGIN_LOG("Constructing PluginPackageAndroid\n");
if(android::WebCoreJni::getJavaVM()->GetEnv((void**) &m_env,
JNI_VERSION_1_4) != JNI_OK) {
PLUGIN_LOG("GetEnv failed!");
}
}
PluginPackageAndroid::~PluginPackageAndroid()
{
PLUGIN_LOG("Destructing PluginPackageAndroid\n");
unload();
}
void PluginPackageAndroid::initializeBrowserFuncs()
{
// Initialize the NPN function pointers that we hand over to the
// plugin.
memset(&m_browserFuncs, 0, sizeof(m_browserFuncs));
m_browserFuncs.size = sizeof(m_browserFuncs);
m_browserFuncs.version = NP_VERSION_MINOR;
m_browserFuncs.geturl = NPN_GetURL;
m_browserFuncs.posturl = NPN_PostURL;
m_browserFuncs.requestread = NPN_RequestRead;
m_browserFuncs.newstream = NPN_NewStream;
m_browserFuncs.write = NPN_Write;
m_browserFuncs.destroystream = NPN_DestroyStream;
m_browserFuncs.status = NPN_Status;
m_browserFuncs.uagent = NPN_UserAgent;
m_browserFuncs.memalloc = NPN_MemAlloc;
m_browserFuncs.memfree = NPN_MemFree;
m_browserFuncs.memflush = NPN_MemFlush;
m_browserFuncs.reloadplugins = NPN_ReloadPlugins;
m_browserFuncs.geturlnotify = NPN_GetURLNotify;
m_browserFuncs.posturlnotify = NPN_PostURLNotify;
m_browserFuncs.getvalue = NPN_GetValue;
m_browserFuncs.setvalue = NPN_SetValue;
m_browserFuncs.invalidaterect = NPN_InvalidateRect;
m_browserFuncs.invalidateregion = NPN_InvalidateRegion;
m_browserFuncs.forceredraw = NPN_ForceRedraw;
m_browserFuncs.getJavaEnv = NPN_GetJavaEnv;
m_browserFuncs.getJavaPeer = NPN_GetJavaPeer;
m_browserFuncs.pushpopupsenabledstate = NPN_PushPopupsEnabledState;
m_browserFuncs.poppopupsenabledstate = NPN_PopPopupsEnabledState;
m_browserFuncs.pluginthreadasynccall = NPN_PluginThreadAsyncCall;
m_browserFuncs.releasevariantvalue = _NPN_ReleaseVariantValue;
m_browserFuncs.getstringidentifier = _NPN_GetStringIdentifier;
m_browserFuncs.getstringidentifiers = _NPN_GetStringIdentifiers;
m_browserFuncs.getintidentifier = _NPN_GetIntIdentifier;
m_browserFuncs.identifierisstring = _NPN_IdentifierIsString;
m_browserFuncs.utf8fromidentifier = _NPN_UTF8FromIdentifier;
m_browserFuncs.intfromidentifier = _NPN_IntFromIdentifier;
m_browserFuncs.createobject = _NPN_CreateObject;
m_browserFuncs.retainobject = _NPN_RetainObject;
m_browserFuncs.releaseobject = _NPN_ReleaseObject;
m_browserFuncs.invoke = _NPN_Invoke;
m_browserFuncs.invokeDefault = _NPN_InvokeDefault;
m_browserFuncs.evaluate = _NPN_Evaluate;
m_browserFuncs.getproperty = _NPN_GetProperty;
m_browserFuncs.setproperty = _NPN_SetProperty;
m_browserFuncs.removeproperty = _NPN_RemoveProperty;
m_browserFuncs.hasproperty = _NPN_HasProperty;
m_browserFuncs.hasmethod = _NPN_HasMethod;
m_browserFuncs.setexception = _NPN_SetException;
m_browserFuncs.enumerate = _NPN_Enumerate;
}
jobject PluginPackageAndroid::createPluginObject(const char *name,
const char *path,
const char *fileName,
const char *description)
{
// Create a Java "class Plugin" object instance
jclass pluginClass = m_env->FindClass("android/webkit/Plugin");
if(!pluginClass) {
PLUGIN_LOG("Couldn't find class android.webkit.Plugin\n");
return 0;
}
// Get Plugin(String, String, String, String, Context)
jmethodID pluginConstructor = m_env->GetMethodID(
pluginClass,
"<init>",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
if(!pluginConstructor) {
PLUGIN_LOG("Couldn't get android.webkit.Plugin constructor\n");
return 0;
}
// Make Java strings of name, path, fileName, description
jstring javaName = m_env->NewStringUTF(name);
jstring javaPath = m_env->NewStringUTF(m_path.utf8().data());
jstring javaFileName = m_env->NewStringUTF(m_fileName.utf8().data());
jstring javaDescription = m_env->NewStringUTF(description);
// Make a plugin instance
jobject pluginObject = m_env->NewObject(pluginClass,
pluginConstructor,
javaName,
javaPath,
javaFileName,
javaDescription);
return pluginObject;
}
jobject PluginPackageAndroid::getPluginListObject()
{
// Get WebView.getPluginList()
jclass webViewClass = m_env->FindClass("android/webkit/WebView");
if(!webViewClass) {
PLUGIN_LOG("Couldn't find class android.webkit.WebView\n");
return 0;
}
jmethodID getPluginList = m_env->GetStaticMethodID(
webViewClass,
"getPluginList",
"()Landroid/webkit/PluginList;");
if(!getPluginList) {
PLUGIN_LOG("Couldn't find android.webkit.WebView.getPluginList()\n");
return 0;
}
// Get the PluginList instance
jobject pluginListObject = m_env->CallStaticObjectMethod(webViewClass,
getPluginList);
if(!pluginListObject) {
PLUGIN_LOG("Couldn't get PluginList object\n");
return 0;
}
return pluginListObject;
}
bool PluginPackageAndroid::addPluginObjectToList(jobject pluginList,
jobject plugin)
{
// Add the Plugin object
jclass pluginListClass = m_env->FindClass("android/webkit/PluginList");
if(!pluginListClass) {
PLUGIN_LOG("Couldn't find class android.webkit.PluginList\n");
return false;
}
jmethodID addPlugin = m_env->GetMethodID(
pluginListClass,
"addPlugin",
"(Landroid/webkit/Plugin;)V");
if(!addPlugin) {
PLUGIN_LOG("Couldn't find android.webkit.PluginList.addPlugin()\n");
return false;
}
m_env->CallVoidMethod(pluginList,
addPlugin,
plugin);
return true;
}
void PluginPackageAndroid::removePluginObjectFromList(jobject pluginList,
jobject plugin)
{
// Remove the Plugin object
jclass pluginListClass = m_env->FindClass("android/webkit/PluginList");
if(!pluginListClass) {
PLUGIN_LOG("Couldn't find class android.webkit.PluginList\n");
return;
}
jmethodID removePlugin = m_env->GetMethodID(
pluginListClass,
"removePlugin",
"(Landroid/webkit/Plugin;)V");
if(!removePlugin) {
PLUGIN_LOG("Couldn't find android.webkit.PluginList.removePlugin()\n");
return;
}
m_env->CallVoidMethod(pluginList,
removePlugin,
plugin);
}
bool PluginPackageAndroid::load()
{
if(m_libraryHandle) {
PLUGIN_LOG("Plugin \"%s\" already loaded\n", m_path.utf8().data());
return true;
}
PLUGIN_LOG("Loading \"%s\"\n", m_path.utf8().data());
// Open the library
void *handle = dlopen(m_path.utf8().data(), RTLD_NOW);
if(!handle) {
PLUGIN_LOG("Couldn't load plugin library \"%s\": %s\n",
m_path.utf8().data(), dlerror());
return false;
}
m_libraryHandle = handle;
// This object will call dlclose() and set m_libraryHandle to NULL
// when going out of scope.
DynamicLibraryCloser dlCloser(this);
// Get the four entry points we need for Linux Netscape Plug-ins
if(!getEntryPoint("NP_Initialize", (void **) &m_NP_Initialize) ||
!getEntryPoint("NP_Shutdown", (void **) &m_NP_Shutdown) ||
!getEntryPoint("NP_GetMIMEDescription",
(void **) &m_NP_GetMIMEDescription) ||
!getEntryPoint("NP_GetValue", (void **) &m_NP_GetValue)) {
// If any of those failed to resolve, fail the entire load
return false;
}
// Get the plugin name and description using NP_GetValue
const char *name;
const char *description;
if(m_NP_GetValue(NULL, NPPVpluginNameString,
&name) != NPERR_NO_ERROR ||
m_NP_GetValue(NULL, NPPVpluginDescriptionString,
&description) != NPERR_NO_ERROR) {
PLUGIN_LOG("Couldn't get name/description using NP_GetValue\n");
return false;
}
PLUGIN_LOG("Plugin name: \"%s\"\n", name);
PLUGIN_LOG("Plugin description: \"%s\"\n", description);
m_name = name;
m_description = description;
// fileName is just the trailing part of the path
int last_slash = m_path.reverseFind('/');
if(last_slash < 0)
m_fileName = m_path;
else
m_fileName = m_path.substring(last_slash + 1);
// Grab the MIME description. This is in the format, e.g:
// application/x-somescriptformat:ssf:Some Script Format
const char *mime_description = m_NP_GetMIMEDescription();
if(!initializeMIMEDescription(mime_description)) {
PLUGIN_LOG("Bad MIME description: \"%s\"\n",
mime_description);
return false;
}
// Create a new Java Plugin object.
jobject pluginObject = createPluginObject(name,
m_path.utf8().data(),
m_fileName.utf8().data(),
description);
if(!pluginObject) {
PLUGIN_LOG("Couldn't create Java Plugin\n");
return false;
}
// Provide the plugin with our browser function table and grab its
// plugin table. Provide the Java environment and the Plugin which
// can be used to override the defaults if the plugin wants.
initializeBrowserFuncs();
memset(&m_pluginFuncs, 0, sizeof(m_pluginFuncs));
m_pluginFuncs.size = sizeof(m_pluginFuncs);
if(m_NP_Initialize(&m_browserFuncs,
&m_pluginFuncs,
m_env,
pluginObject) != NPERR_NO_ERROR) {
PLUGIN_LOG("Couldn't initialize plugin\n");
return false;
}
// Add the Plugin to the PluginList
jobject pluginListObject = getPluginListObject();
if(!pluginListObject) {
PLUGIN_LOG("Couldn't get PluginList object\n");
m_NP_Shutdown();
return false;
}
if(!addPluginObjectToList(pluginListObject, pluginObject)) {
PLUGIN_LOG("Couldn't add Plugin to PluginList\n");
m_NP_Shutdown();
return false;
}
// Retain the Java Plugin object
m_pluginObject = m_env->NewGlobalRef(pluginObject);
// Need to call NP_Shutdown at some point in the future.
m_libraryInitialized = true;
// Don't close the library - loaded OK.
dlCloser.ok();
// Retain the handle so we can close it in the future.
m_libraryHandle = handle;
PLUGIN_LOG("Loaded OK\n");
return true;
}
void PluginPackageAndroid::unload()
{
if(!m_libraryHandle) {
PLUGIN_LOG("Plugin \"%s\" already unloaded\n", m_path.utf8().data());
return; // No library loaded
}
PLUGIN_LOG("Unloading \"%s\"\n", m_path.utf8().data());
if(m_libraryInitialized) {
// Shutdown the plug-in
ASSERT(m_NP_Shutdown != NULL);
PLUGIN_LOG("Calling NP_Shutdown\n");
NPError err = m_NP_Shutdown();
if(err != NPERR_NO_ERROR)
PLUGIN_LOG("Couldn't shutdown plug-in \"%s\"\n",
m_path.utf8().data());
m_libraryInitialized = false;
// Remove the plugin from the PluginList
if(m_pluginObject) {
jobject pluginListObject = getPluginListObject();
if(pluginListObject) {
removePluginObjectFromList(pluginListObject, m_pluginObject);
}
// Remove a reference to the Plugin object so it can
// garbage collect.
m_env->DeleteGlobalRef(m_pluginObject);
m_pluginObject = 0;
}
}
PLUGIN_LOG("Closing library\n");
dlclose(m_libraryHandle);
m_libraryHandle = 0;
memset(&m_browserFuncs, 0, sizeof(m_browserFuncs));
memset(&m_pluginFuncs, 0, sizeof(m_pluginFuncs));
m_NP_Initialize = 0;
m_NP_Shutdown = 0;
m_NP_GetMIMEDescription = 0;
m_NP_GetValue = 0;
}
const NPPluginFuncs* PluginPackageAndroid::pluginFuncs() const
{
ASSERT(m_libraryHandle && m_libraryInitialized);
return &m_pluginFuncs;
}
PluginPackageAndroid* PluginPackageAndroid::createPackage(const String& path,
uint32 lastModified)
{
PluginPackageAndroid* package = new PluginPackageAndroid(path,
lastModified);
if (!package->load()) {
delete package;
return 0;
}
return package;
}
bool PluginPackageAndroid::getEntryPoint(const char *name, void **entry_point)
{
dlerror();
*entry_point = dlsym(m_libraryHandle, name);
const char *error = dlerror();
if(error == NULL && *entry_point != NULL) {
return true;
} else {
PLUGIN_LOG("Couldn't get entry point \"%s\": %s\n",
name, error);
return false;
}
}
bool PluginPackageAndroid::initializeMIMEEntry(const String& mimeEntry)
{
// Each part is split into 3 fields separated by colons
// Field 1 is the MIME type (e.g "application/x-shockwave-flash").
// Field 2 is a comma separated list of file extensions.
// Field 3 is a human readable short description.
Vector<String> fields = mimeEntry.split(':', true);
if(fields.size() != 3)
return false;
const String& mimeType = fields[0];
Vector<String> extensions = fields[1].split(',', true);
const String& description = fields[2];
PLUGIN_LOG("mime_type: \"%s\"\n",
mimeType.utf8().data());
PLUGIN_LOG("extensions: \"%s\"\n",
fields[1].utf8().data());
PLUGIN_LOG("description: \"%s\"\n",
description.utf8().data());
// Map the mime type to the vector of extensions and the description
if(!extensions.isEmpty())
m_mimeToExtensions.set(mimeType, extensions);
if(!description.isEmpty())
m_mimeToDescriptions.set(mimeType, description);
return true;
}
bool PluginPackageAndroid::initializeMIMEDescription(
const String& mimeDescription)
{
PLUGIN_LOG("MIME description: \"%s\"\n", mimeDescription.utf8().data());
// Clear out the current mappings.
m_mimeToDescriptions.clear();
m_mimeToExtensions.clear();
// Split the description into its component entries, separated by
// semicolons.
Vector<String> mimeEntries = mimeDescription.split(';', true);
// Iterate through the entries, adding them to the MIME mappings.
for(Vector<String>::const_iterator it = mimeEntries.begin();
it != mimeEntries.end(); ++it) {
if(!initializeMIMEEntry(*it)) {
PLUGIN_LOG("Bad MIME entry: \"%s\"\n", it->utf8().data());
m_mimeToDescriptions.clear();
m_mimeToExtensions.clear();
return false;
}
}
return true;
}
PluginPackageAndroid::DynamicLibraryCloser::~DynamicLibraryCloser()
{
// Close the PluginPackageAndroid's library
if(m_package)
{
if(m_package->m_libraryHandle)
{
dlclose(m_package->m_libraryHandle);
m_package->m_libraryHandle = NULL;
}
m_package = NULL;
}
}
} // namespace WebCore
#endif