#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 #include #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, "", "(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 fields = mimeEntry.split(':', true); if(fields.size() != 3) return false; const String& mimeType = fields[0]; Vector 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 mimeEntries = mimeDescription.split(';', true); // Iterate through the entries, adding them to the MIME mappings. for(Vector::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