diff options
Diffstat (limited to 'Source/WebCore/plugins')
-rw-r--r-- | Source/WebCore/plugins/android/PluginClient.h | 45 | ||||
-rw-r--r-- | Source/WebCore/plugins/android/PluginDataAndroid.cpp | 69 | ||||
-rw-r--r-- | Source/WebCore/plugins/android/PluginPackageAndroid.cpp | 417 | ||||
-rw-r--r-- | Source/WebCore/plugins/android/PluginViewAndroid.cpp | 735 |
4 files changed, 1266 insertions, 0 deletions
diff --git a/Source/WebCore/plugins/android/PluginClient.h b/Source/WebCore/plugins/android/PluginClient.h new file mode 100644 index 0000000..e3f7ddf --- /dev/null +++ b/Source/WebCore/plugins/android/PluginClient.h @@ -0,0 +1,45 @@ +/* + * Copyright 2009, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER 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. + */ + +#ifndef PLUGINCLIENT_H_ +#define PLUGINCLIENT_H_ + +#include "PlatformString.h" +#include <wtf/Vector.h> + +using namespace WebCore; + +namespace android { + + class PluginClient + { + public: + virtual ~PluginClient() {} + virtual Vector<String> getPluginDirectories() = 0; + virtual String getPluginSharedDataDirectory() = 0; + }; +} + +#endif /* PLUGINCLIENT_H_ */ diff --git a/Source/WebCore/plugins/android/PluginDataAndroid.cpp b/Source/WebCore/plugins/android/PluginDataAndroid.cpp new file mode 100644 index 0000000..f46f0f6 --- /dev/null +++ b/Source/WebCore/plugins/android/PluginDataAndroid.cpp @@ -0,0 +1,69 @@ +/* + * Copyright 2009, The Android Open Source Project + * Copyright (C) 2008 Trolltech ASA + * 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: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER 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 "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; + 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; + mime.type = it->first; + mime.desc = it->second; + mime.extensions = package->mimeToExtensions().get(mime.type); + info.mimes.append(mime); + } + + m_plugins.append(info); + } +} + +void PluginData::refresh() +{ + PluginDatabase *db = PluginDatabase::installedPlugins(); + db->refresh(); +} + +}; diff --git a/Source/WebCore/plugins/android/PluginPackageAndroid.cpp b/Source/WebCore/plugins/android/PluginPackageAndroid.cpp new file mode 100644 index 0000000..24de122 --- /dev/null +++ b/Source/WebCore/plugins/android/PluginPackageAndroid.cpp @@ -0,0 +1,417 @@ +/* + * 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: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER 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 "PluginPackage.h" + +#ifdef ANDROID_PLUGINS + +#include "JNIUtility.h" +#include "PlatformString.h" +#include "PluginDatabase.h" +#include "PluginMainThreadScheduler.h" +#include "Timer.h" +#include "npfunctions.h" +#include "npruntime_impl.h" +#include <dlfcn.h> +#include <errno.h> +#include <wtf/text/CString.h> + +// un-comment this to enable logging +//#define PLUGIN_DEBUG_LOCAL +#define LOG_TAG "WebKit" +#include "NotImplemented.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; + } +} + +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 initializeExtraBrowserFuncs(NPNetscapeFuncs *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; +} + +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; + } else { + 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(m_module, "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(); + // call this afterwards, which may re-initialize some methods, but ensures + // that any additional (or changed) procs are set. There is no real attempt + // to have this step be minimal (i.e. only what we add/override), since the + // core version (initializeBrowserFuncs) can change in the future. + initializeExtraBrowserFuncs(&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()) != NPERR_NO_ERROR) { + PLUGIN_LOG("Couldn't initialize plugin\n"); + return false; + } + + // Don't close the library - loaded OK. + dlCloser.ok(); + m_isLoaded = true; + ++m_loadCount; + PLUGIN_LOG("Initial load ok, count now %d\n", m_loadCount); + return true; +} + +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); + } + + 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; +} + +uint16_t PluginPackage::NPVersion() const +{ + return NP_VERSION_MINOR; +} + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/plugins/android/PluginViewAndroid.cpp b/Source/WebCore/plugins/android/PluginViewAndroid.cpp new file mode 100644 index 0000000..6c712d0 --- /dev/null +++ b/Source/WebCore/plugins/android/PluginViewAndroid.cpp @@ -0,0 +1,735 @@ +/* + * 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: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 THE COPYRIGHT HOLDERS ``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 THE COPYRIGHT OWNER 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 "FocusController.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 "NetworkStateNotifier.h" +#include "NotImplemented.h" +#include "Page.h" +#include "PlatformGraphicsContext.h" +#include "PlatformKeyboardEvent.h" +#include "PluginMainThreadScheduler.h" +#include "PluginPackage.h" +#include "Touch.h" +#include "TouchEvent.h" +#include "TouchList.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" + +#if USE(JSC) +#include <runtime/JSLock.h> +#endif + +#include <wtf/ASCIICType.h> +// #include "runtime.h" +#include "WebViewCore.h" + +/* Controls the printing of log messages in this file. This must be defined + before PluginDebugAndroid.h is included. + */ +// #define PLUGIN_DEBUG_LOCAL +#define TRACE_KEY_EVENTS 0 + +#include "PluginDebug.h" +#include "PluginDebugAndroid.h" +#include "PluginViewBridgeAndroid.h" +#include "PluginWidgetAndroid.h" + +#include "android_npapi.h" +#include "ANPOpenGL_npapi.h" +#include "ANPSurface_npapi.h" +#include "ANPSystem_npapi.h" +#include "ANPVideo_npapi.h" +#include "SkANP.h" +#include "SkFlipPixelRef.h" + +/////////////////////////////////////////////////////////////////////////////// + +extern void ANPAudioTrackInterfaceV0_Init(ANPInterface* value); +extern void ANPAudioTrackInterfaceV1_Init(ANPInterface* value); +extern void ANPBitmapInterfaceV0_Init(ANPInterface* value); +extern void ANPCanvasInterfaceV0_Init(ANPInterface* value); +extern void ANPEventInterfaceV0_Init(ANPInterface* value); +extern void ANPLogInterfaceV0_Init(ANPInterface* value); +extern void ANPMatrixInterfaceV0_Init(ANPInterface* value); +extern void ANPOffscreenInterfaceV0_Init(ANPInterface* value); +extern void ANPPaintInterfaceV0_Init(ANPInterface* value); +extern void ANPPathInterfaceV0_Init(ANPInterface* value); +extern void ANPSurfaceInterfaceV0_Init(ANPInterface* value); +extern void ANPTypefaceInterfaceV0_Init(ANPInterface* value); +extern void ANPWindowInterfaceV0_Init(ANPInterface* value); +extern void ANPWindowInterfaceV1_Init(ANPInterface* value); +extern void ANPSystemInterfaceV0_Init(ANPInterface* value); +extern void ANPSystemInterfaceV1_Init(ANPInterface* value); +extern void ANPSystemInterfaceV2_Init(ANPInterface* value); +extern void ANPOpenGLInterfaceV0_Init(ANPInterface* value); +extern void ANPVideoInterfaceV0_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(AudioTrackInterfaceV1) }, + { VARPROCLINE(BitmapInterfaceV0) }, + { VARPROCLINE(CanvasInterfaceV0) }, + { VARPROCLINE(EventInterfaceV0) }, + { VARPROCLINE(LogInterfaceV0) }, + { VARPROCLINE(MatrixInterfaceV0) }, + { VARPROCLINE(PaintInterfaceV0) }, + { VARPROCLINE(PathInterfaceV0) }, + { VARPROCLINE(SurfaceInterfaceV0) }, + { VARPROCLINE(TypefaceInterfaceV0) }, + { VARPROCLINE(WindowInterfaceV0) }, + { VARPROCLINE(WindowInterfaceV1) }, + { VARPROCLINE(SystemInterfaceV0) }, + { VARPROCLINE(SystemInterfaceV1) }, + { VARPROCLINE(SystemInterfaceV2) }, + { VARPROCLINE(OpenGLInterfaceV0) }, + { VARPROCLINE(VideoInterfaceV0) }, +}; + +/* 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 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; +} + +bool PluginView::platformStart() +{ + return true; +} + +void PluginView::platformDestroy() +{ + delete m_window; +} + +PlatformLayer* PluginView::platformLayer() const +{ + return (PlatformLayer*) m_window->getLayer(); +} + +#if ENABLE(TOUCH_EVENTS) +void PluginView::handleTouchEvent(TouchEvent* event) +{ + if (!m_window->isAcceptingEvent(kTouch_ANPEventFlag)) + return; + + if (!m_window->inFullScreen() && m_parentFrame->document()->focusedNode() != m_element) + return; + + ANPEvent evt; + SkANP::InitEvent(&evt, kMultiTouch_ANPEventType); + + const AtomicString& type = event->type(); + if (eventNames().touchstartEvent == type) + evt.data.multiTouch.action = kDown_ANPTouchAction; + else if (eventNames().touchendEvent == type) + evt.data.multiTouch.action = kUp_ANPTouchAction; + else if (eventNames().touchmoveEvent == type) + evt.data.multiTouch.action = kMove_ANPTouchAction; + else if (eventNames().touchcancelEvent == type) + evt.data.multiTouch.action = kCancel_ANPTouchAction; + else if (eventNames().touchlongpressEvent == type) + evt.data.multiTouch.action = kLongPress_ANPTouchAction; + else if (eventNames().touchdoubletapEvent == type) + evt.data.multiTouch.action = kDoubleTap_ANPTouchAction; + else + return; + + // set the id and timestamp + evt.data.multiTouch.id = 0; // TODO + evt.data.multiTouch.timestamp = 0; // TODO + + // In the event of a touchend (up) or touchcancel event, we must ask the changedTouch for the + // co-ordinates as there is no touch in touches anymore. + TouchList* touches = (evt.data.multiTouch.action == kUp_ANPTouchAction + || evt.data.multiTouch.action == kCancel_ANPTouchAction) ? event->changedTouches() : event->touches(); + + // set each touchPoint + int pointerCount = touches->length(); + evt.data.multiTouch.pointerCount = pointerCount; + evt.data.multiTouch.touchPoint = new TouchPoint[pointerCount]; + + for (int x = 0; x < evt.data.multiTouch.pointerCount; x++) { + Touch* touch = touches->item(x); + // Convert to coordinates that are relative to the plugin. + IntPoint localPos = roundedIntPoint(m_element->renderer()->absoluteToLocal(IntPoint(touch->pageX(), touch->pageY()))); + + evt.data.multiTouch.touchPoint[x].id = touch->identifier(); + evt.data.multiTouch.touchPoint[x].x = localPos.x(); + evt.data.multiTouch.touchPoint[x].y = localPos.y(); + evt.data.multiTouch.touchPoint[x].pressure = 1; // TODO + evt.data.multiTouch.touchPoint[x].size = 1; // TODO + } + + if (m_window->sendEvent(evt)) + event->preventDefault(); + + // cleanup the touch points we allocated + delete[] evt.data.multiTouch.touchPoint; +} +#endif + +void PluginView::handleMouseEvent(MouseEvent* event) +{ + const AtomicString& type = event->type(); + bool isUp = (eventNames().mouseupEvent == type); + bool isDown = (eventNames().mousedownEvent == type); + + ANPEvent evt; + + if (isUp || isDown) { + SkANP::InitEvent(&evt, kMouse_ANPEventType); + evt.data.mouse.action = isUp ? kUp_ANPMouseAction : kDown_ANPMouseAction; + + // Convert to coordinates that are relative to the plugin. + IntPoint localPos = roundedIntPoint(m_element->renderer()->absoluteToLocal(event->absoluteLocation())); + evt.data.mouse.x = localPos.x(); + evt.data.mouse.y = localPos.y(); + + if (isDown) { + // The plugin needs focus to receive keyboard and touch events + m_element->focus(); + event->setDefaultHandled(); + } + } + else { + return; + } + + if (m_window->sendEvent(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::handleFocusEvent(bool hasFocus) +{ + ANPEvent evt; + SkANP::InitEvent(&evt, kLifecycle_ANPEventType); + evt.data.lifecycle.action = hasFocus ? kGainFocus_ANPLifecycleAction : + kLoseFocus_ANPLifecycleAction; + m_window->sendEvent(evt); + + // redraw the plugin which subsequently invalidates the nav cache + IntRect rect = IntRect(m_npWindow.x, m_npWindow.y, + m_npWindow.width, m_npWindow.height); + m_window->webViewCore()->contentInvalidate(rect); +} + +void PluginView::handleKeyboardEvent(KeyboardEvent* event) +{ + if (!m_window->isAcceptingEvent(kKey_ANPEventFlag)) + return; + + const PlatformKeyboardEvent* pke = event->keyEvent(); + if (NULL == pke) { + return; + } + + bool ignoreEvent = false; + + ANPEvent evt; + SkANP::InitEvent(&evt, kKey_ANPEventType); + + switch (pke->type()) { + case PlatformKeyboardEvent::KeyDown: +#if TRACE_KEY_EVENTS + PLUGIN_LOG("--------- KeyDown, ignore\n"); +#endif + ignoreEvent = true; + break; + case PlatformKeyboardEvent::RawKeyDown: + evt.data.key.action = kDown_ANPKeyAction; + break; + case PlatformKeyboardEvent::Char: +#if TRACE_KEY_EVENTS + PLUGIN_LOG("--------- Char, ignore\n"); +#endif + ignoreEvent = true; + break; + case PlatformKeyboardEvent::KeyUp: + evt.data.key.action = kUp_ANPKeyAction; + break; + default: +#if TRACE_KEY_EVENTS + PLUGIN_LOG("------ unexpected keyevent type %d\n", pke->type()); +#endif + ignoreEvent = true; + break; + } + + /* the plugin should be the only party able to return nav control to the + * browser UI. Therefore, if we discard an event on behalf of the plugin + * we should mark the event as being handled. + */ + if (ignoreEvent) { + int keyCode = pke->nativeVirtualKeyCode(); + if (keyCode >= kDpadUp_ANPKeyCode && keyCode <= kDpadCenter_ANPKeyCode) + event->setDefaultHandled(); + 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_window->sendEvent(evt)) { + event->setDefaultHandled(); + } else if (m_window->inFullScreen()){ + // while in the full screen mode, always consumes the key events and + // keeps the document focus + event->setDefaultHandled(); + } else { + // remove the plugin from the document's focus + m_parentFrame->document()->focusedNodeRemoved(); + } +} + +NPError PluginView::handlePostReadFile(Vector<char>& buffer, uint32_t len, const char* buf) +{ + notImplemented(); + return NPERR_GENERIC_ERROR; +} + +bool PluginView::platformGetValueStatic(NPNVariable variable, void* value, NPError* result) +{ + // our interface query is valid with no NPP instance + *result = NPERR_GENERIC_ERROR; + + switch (variable) { + case NPNVisOfflineBool: { + if (value != NULL) { + bool* retValue = static_cast<bool*>(value); + *retValue = !networkStateNotifier().onLine(); + *result = NPERR_NO_ERROR; + return true; + } + break; + } + case kJavaContext_ANPGetValue: { + jobject* retObject = static_cast<jobject*>(value); + *retObject = android::WebViewCore::getApplicationContext(); + *result = NPERR_NO_ERROR; + return true; + } + default: + ; // do nothing + } + + (void)anp_getInterface(variable, value, result); + return true; +} + +void PluginView::setParent(ScrollView* parent) +{ + PLUGIN_LOG("--%p SetParent old=[%p], new=[%p] \n", instance(), this->parent(), parent); + + Widget::setParent(parent); + + if (parent) { + // the widget needs initialized now so that the plugin has access to + // WebViewCore when NPP_New is called + if (m_window && !m_window->webViewCore()) { + android::WebViewCore* c = android::WebViewCore::getWebViewCore(this->parent()); + m_window->init(c); + } + init(); + + /* Our widget needs to recompute its m_windowRect which then sets + the NPWindowRect if necessary. This ensures that if NPWindowRect + is set prior to parent() being set that we still (1) notify the + plugin of its current rect and (2) that we execute our logic in + PluginWidgetAndroid in response to changes to NPWindowRect. + */ + updatePluginWidget(); + } +} + +void PluginView::setNPWindowRect(const IntRect&) +{ + setNPWindowIfNeeded(); +} + +void PluginView::setNPWindowIfNeeded() +{ + PLUGIN_LOG("--%p SetWindow isStarted=[%d] \n", instance(), m_isStarted); + + if (!m_isStarted || !parent()) + return; + + // in Android, plugin always get the setwindow() in the page coordinate. + + // the m_npWindow is relative to the page + m_npWindow.x = m_pageRect.x(); + m_npWindow.y = m_pageRect.y(); + m_npWindow.width = m_pageRect.width(); + m_npWindow.height = m_pageRect.height(); + + m_npWindow.clipRect.left = m_pageRect.x(); + m_npWindow.clipRect.top = m_pageRect.y(); + m_npWindow.clipRect.right = m_pageRect.x() + m_pageRect.width(); + m_npWindow.clipRect.bottom = m_pageRect.y() + m_pageRect.height(); + + if (m_plugin->pluginFuncs()->setwindow) { +#if USE(JSC) + JSC::JSLock::DropAllLocks dropAllLocks(false); +#endif + setCallingPlugin(true); + m_plugin->pluginFuncs()->setwindow(m_instance, &m_npWindow); + setCallingPlugin(false); + } + + m_window->setWindow(&m_npWindow, m_isTransparent); +} + +bool PluginView::platformGetValue(NPNVariable variable, void* value, NPError* result) +{ + 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; + + *result = NPERR_NO_ERROR; + return true; + } + + 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; + + *result = NPERR_NO_ERROR; + return true; + } + + 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(); + *result = NPERR_NO_ERROR; + return true; + } + + case NPNVisOfflineBool: { + if (value == NULL) { + *result = NPERR_GENERIC_ERROR; + return true; + } + bool* retValue = static_cast<bool*>(value); + *retValue = !networkStateNotifier().onLine(); + *result = NPERR_NO_ERROR; + return true; + } + + case kSupportedDrawingModel_ANPGetValue: { + uint32_t* bits = reinterpret_cast<uint32_t*>(value); + *bits = kBitmap_ANPDrawingModel & kSurface_ANPDrawingModel; + *result = NPERR_NO_ERROR; + return true; + } + + case kJavaContext_ANPGetValue: { + jobject* retObject = static_cast<jobject*>(value); + *retObject = android::WebViewCore::getWebViewCore(parent())->getContext(); + *result = NPERR_NO_ERROR; + return true; + } + + default: { + NPError error = NPERR_GENERIC_ERROR; + (void)anp_getInterface(variable, value, &error); + *result = error; + return true; + } + } +} + +NPError PluginView::platformSetValue(NPPVariable variable, void* value) +{ + NPError error = NPERR_GENERIC_ERROR; + + switch (variable) { + case kRequestDrawingModel_ANPSetValue: { + ANPDrawingModel model = reinterpret_cast<ANPDrawingModel>(value); + if (m_window->setDrawingModel(model)) + error = NPERR_NO_ERROR; + break; + } + case kAcceptEvents_ANPSetValue : { + if(value) { + ANPEventFlags flags = *reinterpret_cast<ANPEventFlags*>(value); + m_window->updateEventFlags(flags); + error = NPERR_NO_ERROR; + } + 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); +} + +void PluginView::invalidateRegion(NPRegion region) +{ + // we don't support/define regions (yet), so do nothing +} + +void PluginView::forceRedraw() +{ + this->invalidateRect(0); +} + +void PluginView::setFocus(bool focused) +{ + Widget::setFocus(focused); +// SkDebugf("------------- setFocus %p\n", this); +} + +void PluginView::show() +{ + setSelfVisible(true); + Widget::show(); + + if (platformPluginWidget()) + platformPluginWidget()->layoutSurface(); + +} + +void PluginView::hide() +{ + setSelfVisible(false); + Widget::hide(); + + if (platformPluginWidget()) + platformPluginWidget()->layoutSurface(); +} + +void PluginView::setParentVisible(bool visible) { + + if (isParentVisible() == visible) + return; + + Widget::setParentVisible(visible); + + if (platformPluginWidget()) + platformPluginWidget()->layoutSurface(); + +} + +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()) { + PLUGIN_LOG("--%p FrameRect Dimensions are (0,0).\n", instance()); + return; + } + + if (m_window->isSurfaceDrawingModel()) { + /* the document position of the frame (e.g. iFrame) containing the + surface may have changed, which requires us to to update the global + coordinates of the surface. This is necessary because the plugin has + not moved within its parent frame and therefore will not get any + notification of its global position change. + */ + updatePluginWidget(); + m_window->setSurfaceClip(context->platformContext()->mCanvas->getTotalClip().getBounds()); + } else { + m_window->inval(rect, false); + context->save(); + context->translate(frame.x(), frame.y()); + m_window->draw(android_gc2canvas(context)); + context->restore(); + } + + +} + +void PluginView::updatePluginWidget() +{ + FrameView* frameView = static_cast<FrameView*>(parent()); + PLUGIN_LOG("--%p UpdatePluginWidget frame=[%p] \n", instance(), frameView); + if (frameView) { + m_windowRect = frameView->contentsToWindow(frameRect()); + + IntRect oldPageRect = m_pageRect; + + // only the top ScrollView can have the offset + m_pageRect = m_windowRect; + ScrollView* top = parent(); + while (top->parent()) + top = top->parent(); + m_pageRect.move(top->scrollOffset()); + + if (m_pageRect != oldPageRect) + setNPWindowIfNeeded(); + } +} + +void PluginView::halt() { + notImplemented(); +} + +void PluginView::restart() { + notImplemented(); +} + +} // namespace WebCore |