diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-01-15 16:12:09 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-01-15 16:12:09 -0800 |
commit | 7a355dabbffb876b2e08cf63ac4fc28a39c19c6a (patch) | |
tree | 9e94e34b23ead3c0e7f052ee2d3785404d349981 /WebCore/plugins | |
parent | e933faefa1e899dbd5bf371f499cc682aff46c83 (diff) | |
download | external_webkit-7a355dabbffb876b2e08cf63ac4fc28a39c19c6a.zip external_webkit-7a355dabbffb876b2e08cf63ac4fc28a39c19c6a.tar.gz external_webkit-7a355dabbffb876b2e08cf63ac4fc28a39c19c6a.tar.bz2 |
auto import from //branches/cupcake/...@126645
Diffstat (limited to 'WebCore/plugins')
-rw-r--r-- | WebCore/plugins/android/PluginDataAndroid.cpp | 73 | ||||
-rw-r--r-- | WebCore/plugins/android/PluginPackageAndroid.cpp | 582 | ||||
-rw-r--r-- | WebCore/plugins/android/PluginViewAndroid.cpp | 569 |
3 files changed, 1224 insertions, 0 deletions
diff --git a/WebCore/plugins/android/PluginDataAndroid.cpp b/WebCore/plugins/android/PluginDataAndroid.cpp new file mode 100644 index 0000000..23bed89 --- /dev/null +++ b/WebCore/plugins/android/PluginDataAndroid.cpp @@ -0,0 +1,73 @@ +/* + * Copyright 2009, The Android Open Source Project + Copyright (C) 2008 Trolltech ASA + Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config.h" +#include "PluginData.h" + +#include "PluginDatabase.h" +#include "PluginPackage.h" + +namespace WebCore { + +void PluginData::initPlugins() +{ + PluginDatabase *db = PluginDatabase::installedPlugins(); + const Vector<PluginPackage*> &plugins = db->plugins(); + + for (unsigned int i = 0; i < plugins.size(); ++i) { + PluginInfo* info = new PluginInfo; + PluginPackage* package = plugins[i]; + + info->name = package->name(); + info->file = package->fileName(); + info->desc = package->description(); + + const MIMEToDescriptionsMap& mimeToDescriptions = package->mimeToDescriptions(); + MIMEToDescriptionsMap::const_iterator end = mimeToDescriptions.end(); + for (MIMEToDescriptionsMap::const_iterator it = mimeToDescriptions.begin(); it != end; ++it) { + MimeClassInfo* mime = new MimeClassInfo; + info->mimes.append(mime); + + mime->type = it->first; + mime->desc = it->second; + mime->plugin = info; + + Vector<String> extensions = package->mimeToExtensions().get(mime->type); + + for (unsigned i = 0; i < extensions.size(); i++) { + if (i > 0) + mime->suffixes += ","; + + mime->suffixes += extensions[i]; + } + } + + m_plugins.append(info); + } +} + +void PluginData::refresh() +{ + PluginDatabase *db = PluginDatabase::installedPlugins(); + db->refresh(); +} + +}; diff --git a/WebCore/plugins/android/PluginPackageAndroid.cpp b/WebCore/plugins/android/PluginPackageAndroid.cpp new file mode 100644 index 0000000..aac687c --- /dev/null +++ b/WebCore/plugins/android/PluginPackageAndroid.cpp @@ -0,0 +1,582 @@ +/* + * Copyright 2009, The Android Open Source Project + * 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. + */ + +#ifdef ANDROID_PLUGINS + +#define LOG_TAG "WebKit" + +#include "config.h" +#include "PluginDatabase.h" +#include "PluginPackage.h" + +#include "Timer.h" +#include "PlatformString.h" +#include "PluginMainThreadScheduler.h" +#include "CString.h" +#include "jni_utility.h" +#include "npruntime_impl.h" +#include "npfunctions.h" +#include <dlfcn.h> +#include <errno.h> + +#include "PluginDebug.h" +#include "PluginDebugAndroid.h" + +namespace WebCore { + +// Simple class which calls dlclose() on a dynamic library when going +// out of scope. Call ok() if the handle should stay open. +class DynamicLibraryCloser +{ + public: + DynamicLibraryCloser(PlatformModule *module) : m_module(module) { } + ~DynamicLibraryCloser() + { + // Close the library if non-NULL reference and open. + if (m_module && *m_module) + { + dlclose(*m_module); + *m_module = 0; + } + } + void ok() { m_module = NULL; } + + private: + PlatformModule *m_module; +}; + +// A container for a dummy npp instance. This is used to allow +// NPN_PluginThreadAsyncCall() to be used with NULL passed as the npp +// instance. This is substituted instead, and is shared between all +// plugins which behave in this way. This will be lazily created in +// the first call to NPN_PluginThreadAsyncCall(). +class DummyNpp { + public: + DummyNpp() { + m_npp = new NPP_t(); + m_npp->pdata = NULL; + m_npp->ndata = NULL; + PluginMainThreadScheduler::scheduler().registerPlugin(m_npp); + } + ~DummyNpp() { + PluginMainThreadScheduler::scheduler().unregisterPlugin(m_npp); + delete m_npp; + } + NPP_t *getInstance() { return m_npp; } + + private: + NPP_t *m_npp; +}; + +static bool getEntryPoint(PlatformModule module, + const char *name, + void **entry_point) +{ + dlerror(); + *entry_point = dlsym(module, 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; + } +} + +int PluginPackage::compareFileVersion( + const PlatformModuleVersion& compareVersion) const +{ + // return -1, 0, or 1 if plug-in version is less than, equal to, + // or greater than the passed version + if (m_moduleVersion != compareVersion) + return m_moduleVersion > compareVersion ? 1 : -1; + else + return 0; +} + +bool PluginPackage::isPluginBlacklisted() +{ + // No blacklisted Android plugins... yet! + return false; +} + +void PluginPackage::determineQuirks(const String& mimeType) +{ + // The Gears implementation relies on it being loaded *all the time*, + // so check to see if this package represents the Gears plugin and + // load it. + if (mimeType == "application/x-googlegears") { + m_quirks.add(PluginQuirkDontUnloadPlugin); + } +} + +static void Android_NPN_PluginThreadAsyncCall(NPP instance, + void (*func) (void *), + void *userData) +{ + // Translate all instance == NULL to a dummy actual npp. + static DummyNpp dummyNpp; + if (instance == NULL) { + instance = dummyNpp.getInstance(); + } + // Call through to the wrapped function. + NPN_PluginThreadAsyncCall(instance, func, userData); +} + +static void initializeBrowserFuncs(NPNetscapeFuncs *funcs) +{ + // Initialize the NPN function pointers that we hand over to the + // plugin. + memset(funcs, 0, sizeof(*funcs)); + + funcs->size = sizeof(*funcs); + funcs->version = NP_VERSION_MINOR; + funcs->geturl = NPN_GetURL; + funcs->posturl = NPN_PostURL; + funcs->requestread = NPN_RequestRead; + funcs->newstream = NPN_NewStream; + funcs->write = NPN_Write; + funcs->destroystream = NPN_DestroyStream; + funcs->status = NPN_Status; + funcs->uagent = NPN_UserAgent; + funcs->memalloc = NPN_MemAlloc; + funcs->memfree = NPN_MemFree; + funcs->memflush = NPN_MemFlush; + funcs->reloadplugins = NPN_ReloadPlugins; + funcs->geturlnotify = NPN_GetURLNotify; + funcs->posturlnotify = NPN_PostURLNotify; + funcs->getvalue = NPN_GetValue; + funcs->setvalue = NPN_SetValue; + funcs->invalidaterect = NPN_InvalidateRect; + funcs->invalidateregion = NPN_InvalidateRegion; + funcs->forceredraw = NPN_ForceRedraw; + funcs->getJavaEnv = NPN_GetJavaEnv; + funcs->getJavaPeer = NPN_GetJavaPeer; + funcs->pushpopupsenabledstate = NPN_PushPopupsEnabledState; + funcs->poppopupsenabledstate = NPN_PopPopupsEnabledState; + funcs->pluginthreadasynccall = Android_NPN_PluginThreadAsyncCall; + funcs->scheduletimer = NPN_ScheduleTimer; + funcs->unscheduletimer = NPN_UnscheduleTimer; + + funcs->releasevariantvalue = _NPN_ReleaseVariantValue; + funcs->getstringidentifier = _NPN_GetStringIdentifier; + funcs->getstringidentifiers = _NPN_GetStringIdentifiers; + funcs->getintidentifier = _NPN_GetIntIdentifier; + funcs->identifierisstring = _NPN_IdentifierIsString; + funcs->utf8fromidentifier = _NPN_UTF8FromIdentifier; + funcs->intfromidentifier = _NPN_IntFromIdentifier; + funcs->createobject = _NPN_CreateObject; + funcs->retainobject = _NPN_RetainObject; + funcs->releaseobject = _NPN_ReleaseObject; + funcs->invoke = _NPN_Invoke; + funcs->invokeDefault = _NPN_InvokeDefault; + funcs->evaluate = _NPN_Evaluate; + funcs->getproperty = _NPN_GetProperty; + funcs->setproperty = _NPN_SetProperty; + funcs->removeproperty = _NPN_RemoveProperty; + funcs->hasproperty = _NPN_HasProperty; + funcs->hasmethod = _NPN_HasMethod; + funcs->setexception = _NPN_SetException; + funcs->enumerate = _NPN_Enumerate; +} + +static jobject createPluginObject(const char *name, + const char *path, + const char *fileName, + const char *description) +{ + JNIEnv *env = JSC::Bindings::getJNIEnv(); + // Create a Java "class Plugin" object instance + jclass pluginClass = 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 = 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 = env->NewStringUTF(name); + jstring javaPath = env->NewStringUTF(path); + jstring javaFileName = env->NewStringUTF(fileName); + jstring javaDescription = env->NewStringUTF(description); + // Make a plugin instance + jobject pluginObject = env->NewObject(pluginClass, + pluginConstructor, + javaName, + javaPath, + javaFileName, + javaDescription); + return pluginObject; +} + +static jobject getPluginListObject() +{ + JNIEnv *env = JSC::Bindings::getJNIEnv(); + // Get WebView.getPluginList() + jclass webViewClass = env->FindClass("android/webkit/WebView"); + if(!webViewClass) { + PLUGIN_LOG("Couldn't find class android.webkit.WebView\n"); + return 0; + } + jmethodID getPluginList = 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 = env->CallStaticObjectMethod(webViewClass, + getPluginList); + if(!pluginListObject) { + PLUGIN_LOG("Couldn't get PluginList object\n"); + return 0; + } + return pluginListObject; +} + +static bool addPluginObjectToList(jobject pluginList, jobject plugin) +{ + // Add the Plugin object + JNIEnv *env = JSC::Bindings::getJNIEnv(); + jclass pluginListClass = env->FindClass("android/webkit/PluginList"); + if(!pluginListClass) { + PLUGIN_LOG("Couldn't find class android.webkit.PluginList\n"); + return false; + } + jmethodID addPlugin = env->GetMethodID( + pluginListClass, + "addPlugin", + "(Landroid/webkit/Plugin;)V"); + if(!addPlugin) { + PLUGIN_LOG("Couldn't find android.webkit.PluginList.addPlugin()\n"); + return false; + } + env->CallVoidMethod(pluginList, addPlugin, plugin); + return true; +} + +static void removePluginObjectFromList(jobject pluginList, jobject plugin) +{ + // Remove the Plugin object + JNIEnv *env = JSC::Bindings::getJNIEnv(); + jclass pluginListClass = env->FindClass("android/webkit/PluginList"); + if(!pluginListClass) { + PLUGIN_LOG("Couldn't find class android.webkit.PluginList\n"); + return; + } + jmethodID removePlugin = env->GetMethodID( + pluginListClass, + "removePlugin", + "(Landroid/webkit/Plugin;)V"); + if(!removePlugin) { + PLUGIN_LOG("Couldn't find android.webkit.PluginList.removePlugin()\n"); + return; + } + env->CallVoidMethod(pluginList, removePlugin, plugin); +} + +bool PluginPackage::load() +{ + PLUGIN_LOG("tid:%d isActive:%d isLoaded:%d loadCount:%d\n", + gettid(), + m_freeLibraryTimer.isActive(), + m_isLoaded, + m_loadCount); + if (m_freeLibraryTimer.isActive()) { + ASSERT(m_module); + m_freeLibraryTimer.stop(); + } else if (m_isLoaded) { + if (m_quirks.contains(PluginQuirkDontAllowMultipleInstances)) + return false; + m_loadCount++; + PLUGIN_LOG("Already loaded, count now %d\n", m_loadCount); + return true; + } + ASSERT(m_loadCount == 0); + ASSERT(m_module == NULL); + + 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_module = handle; + PLUGIN_LOG("Fetch Info Loaded %p\n", m_module); + // This object will call dlclose() and set m_module to NULL + // when going out of scope. + DynamicLibraryCloser dlCloser(&m_module); + + + NP_InitializeFuncPtr NP_Initialize; + if(!getEntryPoint(m_module, "NP_Initialize", (void **) &NP_Initialize) || + !getEntryPoint(handle, "NP_Shutdown", (void **) &m_NPP_Shutdown)) { + PLUGIN_LOG("Couldn't find Initialize function\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(&m_browserFuncs); + memset(&m_pluginFuncs, 0, sizeof(m_pluginFuncs)); + m_pluginFuncs.size = sizeof(m_pluginFuncs); + if(NP_Initialize(&m_browserFuncs, + &m_pluginFuncs, + JSC::Bindings::getJNIEnv(), + m_pluginObject) != NPERR_NO_ERROR) { + PLUGIN_LOG("Couldn't initialize plugin\n"); + return false; + } + + // Don't close the library - loaded OK. + dlCloser.ok(); + // Retain the handle so we can close it in the future. + m_module = handle; + m_isLoaded = true; + ++m_loadCount; + PLUGIN_LOG("Initial load ok, count now %d\n", m_loadCount); + return true; +} + +void PluginPackage::unregisterPluginObject() +{ + PLUGIN_LOG("unregisterPluginObject\n"); + // Called by unloadWithoutShutdown(). 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. + JSC::Bindings::getJNIEnv()->DeleteGlobalRef(m_pluginObject); + m_pluginObject = 0; + } +} + +bool PluginPackage::fetchInfo() +{ + PLUGIN_LOG("Fetch Info 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; + } + PLUGIN_LOG("Fetch Info Loaded %p\n", handle); + + // This object will call dlclose() and set m_module to NULL + // when going out of scope. + DynamicLibraryCloser dlCloser(&handle); + + // Get the three entry points we need for Linux Netscape Plug-ins + NP_GetMIMEDescriptionFuncPtr NP_GetMIMEDescription; + NPP_GetValueProcPtr NP_GetValue; + if(!getEntryPoint(handle, "NP_GetMIMEDescription", + (void **) &NP_GetMIMEDescription) || + !getEntryPoint(handle, "NP_GetValue", (void **) &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(NP_GetValue(NULL, NPPVpluginNameString, &name) != NPERR_NO_ERROR || + 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 + String mimeDescription(NP_GetMIMEDescription()); + 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, mimeEntries); + // Iterate through the entries, adding them to the MIME mappings. + for(Vector<String>::const_iterator it = mimeEntries.begin(); + it != mimeEntries.end(); ++it) { + // 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. + const String &mimeEntry = *it; + Vector<String> fields; + mimeEntry.split(':', true, fields); + if(fields.size() != 3) { + PLUGIN_LOG("Bad MIME entry \"%s\"\n", mimeEntry.utf8().data()); + return false; + } + + const String& mimeType = fields[0]; + Vector<String> extensions; + fields[1].split(',', true, extensions); + const String& description = fields[2]; + + determineQuirks(mimeType); + + 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); + } + + // Create a new Java Plugin object, this object is an instance of + // android.os.WebView.Plugin + CString path = m_path.utf8(); + CString filename = m_fileName.utf8(); + jobject pluginObject = createPluginObject(name, + path.data(), + filename.data(), + description); + if(!pluginObject) { + PLUGIN_LOG("Couldn't create Java Plugin\n"); + return false; + } + + // Add the Plugin to the PluginList. This list is used to show the + // user the list of plugins installed in the webkit. + + // The list of plugins are also available from the global static + // function PluginDatabase::installedPlugins(). However, the method + // on WebView to get the plugin list is a static method, and runs in the + // UI thread. We can not easily drop all the GlobalRefs this implementation + // has and switch to just calling through JNI to aforementioned API as + // WebKit runs in another thread and the WebView call would need to change + // to being async. + jobject pluginListObject = getPluginListObject(); + if(!pluginListObject) { + PLUGIN_LOG("Couldn't get PluginList object\n"); + return false; + } + if(!addPluginObjectToList(pluginListObject, pluginObject)) { + PLUGIN_LOG("Couldn't add Plugin to PluginList\n"); + m_NPP_Shutdown(); + return false; + } + + // Retain the Java Plugin object + m_pluginObject = JSC::Bindings::getJNIEnv()->NewGlobalRef(pluginObject); + + PLUGIN_LOG("Fetch Info Loaded plugin details ok \"%s\"\n", + m_path.utf8().data()); + + // If this plugin needs to be kept in memory, unload the module now + // and load it permanently. + if (m_quirks.contains(PluginQuirkDontUnloadPlugin)) { + dlCloser.ok(); + dlclose(handle); + load(); + } + + // dlCloser will unload the plugin if required. + return true; +} + +unsigned PluginPackage::hash() const +{ + const unsigned hashCodes[] = { + m_name.impl()->hash(), + m_description.impl()->hash(), + m_mimeToExtensions.size(), + }; + + return StringImpl::computeHash(reinterpret_cast<const UChar*>(hashCodes), + sizeof(hashCodes) / sizeof(UChar)); +} + +bool PluginPackage::equal(const PluginPackage& a, const PluginPackage& b) +{ + if (a.m_name != b.m_name) + return false; + + if (a.m_description != b.m_description) + return false; + + if (a.m_mimeToExtensions.size() != b.m_mimeToExtensions.size()) + return false; + + MIMEToExtensionsMap::const_iterator::Keys end = + a.m_mimeToExtensions.end().keys(); + for (MIMEToExtensionsMap::const_iterator::Keys it = + a.m_mimeToExtensions.begin().keys(); + it != end; + ++it) { + if (!b.m_mimeToExtensions.contains(*it)) { + return false; + } + } + + return true; +} + +} // namespace WebCore + +#endif diff --git a/WebCore/plugins/android/PluginViewAndroid.cpp b/WebCore/plugins/android/PluginViewAndroid.cpp new file mode 100644 index 0000000..7e3e479 --- /dev/null +++ b/WebCore/plugins/android/PluginViewAndroid.cpp @@ -0,0 +1,569 @@ +/* + * Copyright 2009, The Android Open Source Project + * 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. + */ +#define LOG_TAG "WebCore" + +#include "config.h" +#include "PluginView.h" + +#include "Document.h" +#include "Element.h" +#include "EventNames.h" +#include "FrameLoader.h" +#include "FrameLoadRequest.h" +#include "FrameTree.h" +#include "Frame.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "HTMLNames.h" +#include "HTMLPlugInElement.h" +#include "Image.h" +#include "KeyboardEvent.h" +#include "MIMETypeRegistry.h" +#include "MouseEvent.h" +#include "NotImplemented.h" +#include "Page.h" +#include "PlatformGraphicsContext.h" +#include "PlatformKeyboardEvent.h" +#include "PluginMainThreadScheduler.h" +#include "PluginPackage.h" +// #include "kjs_binding.h" +// #include "kjs_proxy.h" +#include "android_graphics.h" +#include "SkCanvas.h" +#include "npruntime_impl.h" +#include "runtime_root.h" +#include "utils/SystemClock.h" +#include "ScriptController.h" +#include "Settings.h" +#include <runtime/JSLock.h> +// #include <kjs/value.h> +#include <wtf/ASCIICType.h> +#include "runtime.h" +#include "WebViewCore.h" + +#include "PluginDebug.h" +#include "PluginDebugAndroid.h" +#include "PluginViewBridgeAndroid.h" +#include "PluginWidgetAndroid.h" + +#include "android_npapi.h" +#include "SkANP.h" +#include "SkFlipPixelRef.h" + +/////////////////////////////////////////////////////////////////////////////// + +extern void ANPAudioTrackInterfaceV0_Init(ANPInterface* value); +extern void ANPCanvasInterfaceV0_Init(ANPInterface* value); +extern void ANPLogInterfaceV0_Init(ANPInterface* value); +extern void ANPOffscreenInterfaceV0_Init(ANPInterface* value); +extern void ANPPaintInterfaceV0_Init(ANPInterface* value); +extern void ANPTypefaceInterfaceV0_Init(ANPInterface* value); +extern void ANPWindowInterfaceV0_Init(ANPInterface* value); + +struct VarProcPair { + int enumValue; + size_t size; + void (*proc)(ANPInterface*); +}; + +#define VARPROCLINE(name) \ + k##name##_ANPGetValue, sizeof(ANP##name), ANP##name##_Init + +static const VarProcPair gVarProcs[] = { + { VARPROCLINE(AudioTrackInterfaceV0) }, + { VARPROCLINE(LogInterfaceV0) }, + { VARPROCLINE(CanvasInterfaceV0) }, + { VARPROCLINE(PaintInterfaceV0) }, + { VARPROCLINE(TypefaceInterfaceV0) }, + { VARPROCLINE(WindowInterfaceV0) }, +}; + +/* return true if var was an interface request (error will be set accordingly) + return false if var is not a recognized interface (and ignore error param) + */ +static bool anp_getInterface(NPNVariable var, void* value, NPError* error) { + const VarProcPair* iter = gVarProcs; + const VarProcPair* stop = gVarProcs + SK_ARRAY_COUNT(gVarProcs); + while (iter < stop) { + if (iter->enumValue == var) { + ANPInterface* i = reinterpret_cast<ANPInterface*>(value); + if (i->inSize < iter->size) { + SkDebugf("------- interface %d, expected size %d, allocated %d\n", + var, iter->size, i->inSize); + *error = NPERR_INCOMPATIBLE_VERSION_ERROR; + } else { + iter->proc(i); + *error = NPERR_NO_ERROR; + } + return true; + } + iter += 1; + } + SkDebugf("------ unknown NPNVariable %d\n", var); + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +using JSC::ExecState; +using JSC::Interpreter; +using JSC::JSLock; +using JSC::JSObject; +using JSC::JSValue; +using JSC::UString; + +using std::min; + +using namespace WTF; + +namespace WebCore { + +using namespace HTMLNames; + +void PluginView::platformInit() +{ + setPlatformWidget(new PluginViewBridgeAndroid()); + + m_isWindowed = false; // we don't support windowed yet + + m_window = new PluginWidgetAndroid(this); + + m_npWindow.type = NPWindowTypeDrawable; + m_npWindow.window = 0; +} + +PluginView::~PluginView() +{ + stop(); + + deleteAllValues(m_requests); + + freeStringArray(m_paramNames, m_paramCount); + freeStringArray(m_paramValues, m_paramCount); + + m_parentFrame->script()->cleanupScriptObjectsForPlugin(this); + +// Since we have no legacy plugins to check, we ignore the quirks check +// if (m_plugin && !m_plugin->quirks().contains(PluginQuirkDontUnloadPlugin)) + if (m_plugin) { + m_plugin->unload(); + } + delete m_window; +} + +void PluginView::init() +{ + if (m_haveInitialized) + return; + m_haveInitialized = true; + + android::WebViewCore* c = android::WebViewCore::getWebViewCore(this->parent()); + m_window->init(c); + + if (!m_plugin) { + ASSERT(m_status == PluginStatusCanNotFindPlugin); + return; + } + + if (!m_plugin->load()) { + m_plugin = 0; + m_status = PluginStatusCanNotLoadPlugin; + return; + } + + if (!start()) { + m_status = PluginStatusCanNotLoadPlugin; + return; + } + + m_status = PluginStatusLoadedSuccessfully; +} + +void PluginView::handleMouseEvent(MouseEvent* event) +{ + const AtomicString& type = event->type(); + bool isDown = (eventNames().mousedownEvent == type); + bool isUp = (eventNames().mouseupEvent == type); + if (!isDown && !isUp) { + return; + } + + ANPEvent evt; + SkANP::InitEvent(&evt, kTouch_ANPEventType); + + evt.data.touch.action = isDown ? kDown_ANPTouchAction : kUp_ANPTouchAction; + evt.data.touch.modifiers = 0; // todo + // these are relative to plugin + evt.data.touch.x = event->pageX() - m_npWindow.x; + evt.data.touch.y = event->pageY() - m_npWindow.y; + if (m_plugin->pluginFuncs()->event(m_instance, &evt)) { + event->setDefaultHandled(); + } +} + +static ANPKeyModifier make_modifiers(bool shift, bool alt) { + ANPKeyModifier mod = 0; + if (shift) { + mod |= kShift_ANPKeyModifier; + } + if (alt) { + mod |= kAlt_ANPKeyModifier; + } + return mod; +} + +void PluginView::handleKeyboardEvent(KeyboardEvent* event) +{ + const PlatformKeyboardEvent* pke = event->keyEvent(); + if (NULL == pke) { + return; + } + + ANPEvent evt; + SkANP::InitEvent(&evt, kKey_ANPEventType); + + switch (pke->type()) { + case PlatformKeyboardEvent::KeyDown: +#ifdef TRACE_KEY_EVENTS + SkDebugf("--------- KeyDown, ignore\n"); +#endif + return; + case PlatformKeyboardEvent::RawKeyDown: + evt.data.key.action = kDown_ANPKeyAction; + break; + case PlatformKeyboardEvent::Char: +#ifdef TRACE_KEY_EVENTS + SkDebugf("--------- Char, ignore\n"); +#endif + return; + case PlatformKeyboardEvent::KeyUp: + evt.data.key.action = kUp_ANPKeyAction; + break; + default: +#ifdef TRACE_KEY_EVENTS + SkDebugf("------ unexpected keyevent type %d\n", pke->type()); +#endif + return; + } + evt.data.key.nativeCode = pke->nativeVirtualKeyCode(); + evt.data.key.virtualCode = pke->windowsVirtualKeyCode(); + evt.data.key.repeatCount = pke->repeatCount(); + evt.data.key.modifiers = make_modifiers(pke->shiftKey(), pke->altKey()); + evt.data.key.unichar = pke->unichar(); + + if (m_plugin->pluginFuncs()->event(m_instance, &evt)) { + event->setDefaultHandled(); + } +} + +NPError PluginView::handlePostReadFile(Vector<char>& buffer, uint32 len, const char* buf) +{ + notImplemented(); + return NPERR_GENERIC_ERROR; +} + +NPError PluginView::getValueStatic(NPNVariable variable, void* value) +{ + // our interface query is valid with no NPP instance + NPError error = NPERR_GENERIC_ERROR; + (void)anp_getInterface(variable, value, &error); + return error; +} + +void PluginView::setParent(ScrollView* parent) +{ + Widget::setParent(parent); + + if (parent) + init(); +} + +void PluginView::setNPWindowRect(const IntRect& rect) +{ + if (!m_isStarted) + return; + + const int width = rect.width(); + const int height = rect.height(); + + IntPoint p = static_cast<FrameView*>(parent())->contentsToWindow(rect.location()); + m_npWindow.x = p.x(); + m_npWindow.y = p.y(); + + m_npWindow.width = width; + m_npWindow.height = height; + + m_npWindow.clipRect.left = 0; + m_npWindow.clipRect.top = 0; + m_npWindow.clipRect.right = width; + m_npWindow.clipRect.bottom = height; + + if (m_plugin->pluginFuncs()->setwindow) { + JSC::JSLock::DropAllLocks dropAllLocks(false); + setCallingPlugin(true); + m_plugin->pluginFuncs()->setwindow(m_instance, &m_npWindow); + setCallingPlugin(false); + } + + m_window->setWindow(m_npWindow.x, m_npWindow.y, width, height, + m_isTransparent); +} + +void PluginView::stop() +{ + if (!m_isStarted) + return; + + HashSet<RefPtr<PluginStream> > streams = m_streams; + HashSet<RefPtr<PluginStream> >::iterator end = streams.end(); + for (HashSet<RefPtr<PluginStream> >::iterator it = streams.begin(); it != end; ++it) { + (*it)->stop(); + disconnectStream((*it).get()); + } + + ASSERT(m_streams.isEmpty()); + + m_isStarted = false; + + JSC::JSLock::DropAllLocks dropAllLocks(false); + + PluginMainThreadScheduler::scheduler().unregisterPlugin(m_instance); + + // Destroy the plugin + NPSavedData* savedData = 0; + setCallingPlugin(true); + NPError npErr = m_plugin->pluginFuncs()->destroy(m_instance, &savedData); + setCallingPlugin(false); + LOG_NPERROR(npErr); + + if (savedData) { + if (savedData->buf) + NPN_MemFree(savedData->buf); + NPN_MemFree(savedData); + } + + m_instance->pdata = 0; +} + +const char* PluginView::userAgentStatic() +{ + return 0; +} + +const char* PluginView::userAgent() +{ + if (m_userAgent.isNull()) + m_userAgent = m_parentFrame->loader()->userAgent(m_url).utf8(); + return m_userAgent.data(); +} + +NPError PluginView::getValue(NPNVariable variable, void* value) +{ + switch (variable) { + case NPNVWindowNPObject: { + NPObject* windowScriptObject = + m_parentFrame->script()->windowScriptNPObject(); + + // Return value is expected to be retained, as described + // here: + // <http://www.mozilla.org/projects/plugin/npruntime.html> + if (windowScriptObject) + _NPN_RetainObject(windowScriptObject); + + void** v = (void**)value; + *v = windowScriptObject; + + return NPERR_NO_ERROR; + } + + case NPNVPluginElementNPObject: { + NPObject* pluginScriptObject = 0; + + if (m_element->hasTagName(appletTag) || + m_element->hasTagName(embedTag) || + m_element->hasTagName(objectTag)) { + HTMLPlugInElement* pluginElement = + static_cast<HTMLPlugInElement*>(m_element); + pluginScriptObject = pluginElement->getNPObject(); + } + + // Return value is expected to be retained, as described + // here: + // <http://www.mozilla.org/projects/plugin/npruntime.html> + if (pluginScriptObject) + _NPN_RetainObject(pluginScriptObject); + + void** v = (void**)value; + *v = pluginScriptObject; + + return NPERR_NO_ERROR; + } + + case NPNVnetscapeWindow: { + // Return the top level WebView Java object associated + // with this instance. + jobject *retObject = static_cast<jobject*>(value); + *retObject = android::WebViewCore::getWebViewCore(parent())->getWebViewJavaObject(); + return NPERR_NO_ERROR; + } + + case kSupportedDrawingModel_ANPGetValue: { + uint32_t* bits = reinterpret_cast<uint32_t*>(value); + *bits = (1 << kBitmap_ANPDrawingModel); + return NPERR_NO_ERROR; + } + + default: { + NPError error = NPERR_GENERIC_ERROR; + (void)anp_getInterface(variable, value, &error); + return error; + } + } +} + +NPError PluginView::platformSetValue(NPPVariable variable, void* value) +{ + NPError error = NPERR_GENERIC_ERROR; + + switch (variable) { + case kRequestDrawingModel_ANPSetValue: { + ANPDrawingModel model = reinterpret_cast<ANPDrawingModel>(value); + switch (model) { + case kBitmap_ANPDrawingModel: + m_window->setDrawingModel(model); + error = NPERR_NO_ERROR; + break; + default: + break; + } + } + default: + break; + } + return error; +} + +void PluginView::invalidateRect(const IntRect& r) +{ + m_window->inval(r, true); +} + +void PluginView::invalidateRect(NPRect* rect) +{ + IntRect r; + + if (rect) { + r = IntRect(rect->left, rect->top, + rect->right - rect->left, rect->bottom - rect->top); + } else { + r = IntRect(0, 0, m_npWindow.width, m_npWindow.height); + } + + m_window->inval(r, true); +// android::WebViewCore::getWebViewCore(parent())->contentInvalidate(r); +} + +void PluginView::invalidateRegion(NPRegion region) +{ + // we don't support/define regions (yet), so do nothing +} + +void PluginView::forceRedraw() +{ + this->invalidateRect(0); +} + +void PluginView::setFocus() +{ + Widget::setFocus(); +// SkDebugf("------------- setFocus %p\n", this); +} + +void PluginView::show() +{ + setSelfVisible(true); + Widget::show(); +} + +void PluginView::hide() +{ + setSelfVisible(false); + Widget::hide(); +} + +void PluginView::paintMissingPluginIcon(GraphicsContext* context, + const IntRect& rect) +{ + static RefPtr<Image> gNullPluginImage; + if (!gNullPluginImage) { + gNullPluginImage = Image::loadPlatformResource("nullplugin"); + } + Image* image = gNullPluginImage.get(); + + IntRect imageRect(frameRect().x(), frameRect().y(), + image->width(), image->height()); + + int xOffset = (frameRect().width() - imageRect.width()) / 2; + int yOffset = (frameRect().height() - imageRect.height()) / 2; + + imageRect.move(xOffset, yOffset); + + if (!rect.intersects(imageRect)) + return; + + context->drawImage(image, imageRect.location()); +} + +void PluginView::paint(GraphicsContext* context, const IntRect& rect) +{ + if (!m_isStarted) { + // Draw the "missing plugin" image + paintMissingPluginIcon(context, rect); + return; + } + + IntRect frame = frameRect(); + if (!frame.width() || !frame.height()) { + return; + } + + m_window->inval(rect, false); + m_window->draw(android_gc2canvas(context)); +} + +// new as of SVN 38068, Nov 5 2008 +void PluginView::updatePluginWidget() const +{ + notImplemented(); +} + +// new as of SVN 38068, Nov 5 2008 +void PluginView::setParentVisible(bool) { + notImplemented(); +} + +} // namespace WebCore + |