Home | History | Annotate | Download | only in android
      1 /*
      2  * Copyright 2009, The Android Open Source Project
      3  * Copyright (C) 2006, 2007 Apple Inc.  All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  *  * Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  *  * Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in the
     12  *    documentation and/or other materials provided with the distribution.
     13  *
     14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
     15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     25  */
     26 
     27 #include "config.h"
     28 #include "PluginPackage.h"
     29 
     30 #ifdef ANDROID_PLUGINS
     31 
     32 #include "CString.h"
     33 #include "JNIUtility.h"
     34 #include "PlatformString.h"
     35 #include "PluginDatabase.h"
     36 #include "PluginMainThreadScheduler.h"
     37 #include "Timer.h"
     38 #include "npfunctions.h"
     39 #include "npruntime_impl.h"
     40 #include <dlfcn.h>
     41 #include <errno.h>
     42 
     43 // un-comment this to enable logging
     44 //#define PLUGIN_DEBUG_LOCAL
     45 #define LOG_TAG "WebKit"
     46 #include "NotImplemented.h"
     47 #include "PluginDebug.h"
     48 #include "PluginDebugAndroid.h"
     49 
     50 namespace WebCore {
     51 
     52 // Simple class which calls dlclose() on a dynamic library when going
     53 // out of scope. Call ok() if the handle should stay open.
     54 class DynamicLibraryCloser
     55 {
     56   public:
     57     DynamicLibraryCloser(PlatformModule *module) : m_module(module) { }
     58     ~DynamicLibraryCloser()
     59     {
     60         // Close the library if non-NULL reference and open.
     61         if (m_module && *m_module)
     62         {
     63             dlclose(*m_module);
     64             *m_module = 0;
     65         }
     66     }
     67     void ok() { m_module = NULL; }
     68 
     69   private:
     70     PlatformModule *m_module;
     71 };
     72 
     73 // A container for a dummy npp instance. This is used to allow
     74 // NPN_PluginThreadAsyncCall() to be used with NULL passed as the npp
     75 // instance. This is substituted instead, and is shared between all
     76 // plugins which behave in this way. This will be lazily created in
     77 // the first call to NPN_PluginThreadAsyncCall().
     78 class DummyNpp {
     79   public:
     80     DummyNpp() {
     81         m_npp = new NPP_t();
     82         m_npp->pdata = NULL;
     83         m_npp->ndata = NULL;
     84         PluginMainThreadScheduler::scheduler().registerPlugin(m_npp);
     85     }
     86     ~DummyNpp() {
     87         PluginMainThreadScheduler::scheduler().unregisterPlugin(m_npp);
     88         delete m_npp;
     89     }
     90     NPP_t *getInstance() { return m_npp; }
     91 
     92   private:
     93     NPP_t *m_npp;
     94 };
     95 
     96 static bool getEntryPoint(PlatformModule module,
     97                           const char *name,
     98                           void **entry_point)
     99 {
    100     dlerror();
    101     *entry_point = dlsym(module, name);
    102     const char *error = dlerror();
    103     if(error == NULL && *entry_point != NULL) {
    104         return true;
    105     } else {
    106         PLUGIN_LOG("Couldn't get entry point \"%s\": %s\n",
    107                    name, error);
    108         return false;
    109     }
    110 }
    111 
    112 bool PluginPackage::isPluginBlacklisted()
    113 {
    114     // No blacklisted Android plugins... yet!
    115     return false;
    116 }
    117 
    118 void PluginPackage::determineQuirks(const String& mimeType)
    119 {
    120     // The Gears implementation relies on it being loaded *all the time*,
    121     // so check to see if this package represents the Gears plugin and
    122     // load it.
    123     if (mimeType == "application/x-googlegears") {
    124         m_quirks.add(PluginQuirkDontUnloadPlugin);
    125     }
    126 }
    127 
    128 static void Android_NPN_PluginThreadAsyncCall(NPP instance,
    129                                               void (*func) (void *),
    130                                               void *userData)
    131 {
    132     // Translate all instance == NULL to a dummy actual npp.
    133     static DummyNpp dummyNpp;
    134     if (instance == NULL) {
    135         instance = dummyNpp.getInstance();
    136     }
    137     // Call through to the wrapped function.
    138     NPN_PluginThreadAsyncCall(instance, func, userData);
    139 }
    140 
    141 static void initializeExtraBrowserFuncs(NPNetscapeFuncs *funcs)
    142 {
    143     funcs->version = NP_VERSION_MINOR;
    144     funcs->geturl = NPN_GetURL;
    145     funcs->posturl = NPN_PostURL;
    146     funcs->requestread = NPN_RequestRead;
    147     funcs->newstream = NPN_NewStream;
    148     funcs->write = NPN_Write;
    149     funcs->destroystream = NPN_DestroyStream;
    150     funcs->status = NPN_Status;
    151     funcs->uagent = NPN_UserAgent;
    152     funcs->memalloc = NPN_MemAlloc;
    153     funcs->memfree = NPN_MemFree;
    154     funcs->memflush = NPN_MemFlush;
    155     funcs->reloadplugins = NPN_ReloadPlugins;
    156     funcs->geturlnotify = NPN_GetURLNotify;
    157     funcs->posturlnotify = NPN_PostURLNotify;
    158     funcs->getvalue = NPN_GetValue;
    159     funcs->setvalue = NPN_SetValue;
    160     funcs->invalidaterect = NPN_InvalidateRect;
    161     funcs->invalidateregion = NPN_InvalidateRegion;
    162     funcs->forceredraw = NPN_ForceRedraw;
    163     funcs->getJavaEnv = NPN_GetJavaEnv;
    164     funcs->getJavaPeer = NPN_GetJavaPeer;
    165     funcs->pushpopupsenabledstate = NPN_PushPopupsEnabledState;
    166     funcs->poppopupsenabledstate = NPN_PopPopupsEnabledState;
    167     funcs->pluginthreadasynccall = Android_NPN_PluginThreadAsyncCall;
    168     funcs->scheduletimer = NPN_ScheduleTimer;
    169     funcs->unscheduletimer = NPN_UnscheduleTimer;
    170 
    171     funcs->releasevariantvalue = _NPN_ReleaseVariantValue;
    172     funcs->getstringidentifier = _NPN_GetStringIdentifier;
    173     funcs->getstringidentifiers = _NPN_GetStringIdentifiers;
    174     funcs->getintidentifier = _NPN_GetIntIdentifier;
    175     funcs->identifierisstring = _NPN_IdentifierIsString;
    176     funcs->utf8fromidentifier = _NPN_UTF8FromIdentifier;
    177     funcs->intfromidentifier = _NPN_IntFromIdentifier;
    178     funcs->createobject = _NPN_CreateObject;
    179     funcs->retainobject = _NPN_RetainObject;
    180     funcs->releaseobject = _NPN_ReleaseObject;
    181     funcs->invoke = _NPN_Invoke;
    182     funcs->invokeDefault = _NPN_InvokeDefault;
    183     funcs->evaluate = _NPN_Evaluate;
    184     funcs->getproperty = _NPN_GetProperty;
    185     funcs->setproperty = _NPN_SetProperty;
    186     funcs->removeproperty = _NPN_RemoveProperty;
    187     funcs->hasproperty = _NPN_HasProperty;
    188     funcs->hasmethod = _NPN_HasMethod;
    189     funcs->setexception = _NPN_SetException;
    190     funcs->enumerate = _NPN_Enumerate;
    191 }
    192 
    193 bool PluginPackage::load()
    194 {
    195     PLUGIN_LOG("tid:%d isActive:%d isLoaded:%d loadCount:%d\n",
    196                gettid(),
    197                m_freeLibraryTimer.isActive(),
    198                m_isLoaded,
    199                m_loadCount);
    200     if (m_freeLibraryTimer.isActive()) {
    201         ASSERT(m_module);
    202         m_freeLibraryTimer.stop();
    203     } else if (m_isLoaded) {
    204         if (m_quirks.contains(PluginQuirkDontAllowMultipleInstances))
    205             return false;
    206         m_loadCount++;
    207         PLUGIN_LOG("Already loaded, count now %d\n", m_loadCount);
    208         return true;
    209     }
    210     ASSERT(m_loadCount == 0);
    211     ASSERT(m_module == NULL);
    212 
    213     PLUGIN_LOG("Loading \"%s\"\n", m_path.utf8().data());
    214 
    215     // Open the library
    216     void *handle = dlopen(m_path.utf8().data(), RTLD_NOW);
    217     if(!handle) {
    218         PLUGIN_LOG("Couldn't load plugin library \"%s\": %s\n",
    219                    m_path.utf8().data(), dlerror());
    220         return false;
    221     }
    222     m_module = handle;
    223     PLUGIN_LOG("Fetch Info Loaded %p\n", m_module);
    224     // This object will call dlclose() and set m_module to NULL
    225     // when going out of scope.
    226     DynamicLibraryCloser dlCloser(&m_module);
    227 
    228 
    229     NP_InitializeFuncPtr NP_Initialize;
    230     if(!getEntryPoint(m_module, "NP_Initialize", (void **) &NP_Initialize) ||
    231             !getEntryPoint(handle, "NP_Shutdown", (void **) &m_NPP_Shutdown)) {
    232         PLUGIN_LOG("Couldn't find Initialize function\n");
    233         return false;
    234     }
    235 
    236     // Provide the plugin with our browser function table and grab its
    237     // plugin table. Provide the Java environment and the Plugin which
    238     // can be used to override the defaults if the plugin wants.
    239     initializeBrowserFuncs();
    240     // call this afterwards, which may re-initialize some methods, but ensures
    241     // that any additional (or changed) procs are set. There is no real attempt
    242     // to have this step be minimal (i.e. only what we add/override), since the
    243     // core version (initializeBrowserFuncs) can change in the future.
    244     initializeExtraBrowserFuncs(&m_browserFuncs);
    245 
    246     memset(&m_pluginFuncs, 0, sizeof(m_pluginFuncs));
    247     m_pluginFuncs.size = sizeof(m_pluginFuncs);
    248     if(NP_Initialize(&m_browserFuncs,
    249                      &m_pluginFuncs,
    250                      JSC::Bindings::getJNIEnv()) != NPERR_NO_ERROR) {
    251         PLUGIN_LOG("Couldn't initialize plugin\n");
    252         return false;
    253     }
    254 
    255     // Don't close the library - loaded OK.
    256     dlCloser.ok();
    257     // Retain the handle so we can close it in the future.
    258     m_module = handle;
    259     m_isLoaded = true;
    260     ++m_loadCount;
    261     PLUGIN_LOG("Initial load ok, count now %d\n", m_loadCount);
    262     return true;
    263 }
    264 
    265 bool PluginPackage::fetchInfo()
    266 {
    267     PLUGIN_LOG("Fetch Info Loading \"%s\"\n", m_path.utf8().data());
    268 
    269     // Open the library
    270     void *handle = dlopen(m_path.utf8().data(), RTLD_NOW);
    271     if(!handle) {
    272         PLUGIN_LOG("Couldn't load plugin library \"%s\": %s\n",
    273                    m_path.utf8().data(), dlerror());
    274         return false;
    275     }
    276     PLUGIN_LOG("Fetch Info Loaded %p\n", handle);
    277 
    278     // This object will call dlclose() and set m_module to NULL
    279     // when going out of scope.
    280     DynamicLibraryCloser dlCloser(&handle);
    281 
    282     // Get the three entry points we need for Linux Netscape Plug-ins
    283     NP_GetMIMEDescriptionFuncPtr NP_GetMIMEDescription;
    284     NPP_GetValueProcPtr NP_GetValue;
    285     if(!getEntryPoint(handle, "NP_GetMIMEDescription",
    286             (void **) &NP_GetMIMEDescription) ||
    287             !getEntryPoint(handle, "NP_GetValue", (void **) &NP_GetValue)) {
    288         // If any of those failed to resolve, fail the entire load
    289         return false;
    290     }
    291 
    292     // Get the plugin name and description using NP_GetValue
    293     const char *name;
    294     const char *description;
    295     if(NP_GetValue(NULL, NPPVpluginNameString, &name) != NPERR_NO_ERROR ||
    296             NP_GetValue(NULL, NPPVpluginDescriptionString, &description) !=
    297                 NPERR_NO_ERROR) {
    298         PLUGIN_LOG("Couldn't get name/description using NP_GetValue\n");
    299         return false;
    300     }
    301 
    302     PLUGIN_LOG("Plugin name: \"%s\"\n", name);
    303     PLUGIN_LOG("Plugin description: \"%s\"\n", description);
    304     m_name = name;
    305     m_description = description;
    306 
    307     // fileName is just the trailing part of the path
    308     int last_slash = m_path.reverseFind('/');
    309     if(last_slash < 0)
    310         m_fileName = m_path;
    311     else
    312         m_fileName = m_path.substring(last_slash + 1);
    313 
    314     // Grab the MIME description. This is in the format, e.g:
    315     // application/x-somescriptformat:ssf:Some Script Format
    316     String mimeDescription(NP_GetMIMEDescription());
    317     PLUGIN_LOG("MIME description: \"%s\"\n", mimeDescription.utf8().data());
    318     // Clear out the current mappings.
    319     m_mimeToDescriptions.clear();
    320     m_mimeToExtensions.clear();
    321     // Split the description into its component entries, separated by
    322     // semicolons.
    323     Vector<String> mimeEntries;
    324     mimeDescription.split(';', true, mimeEntries);
    325     // Iterate through the entries, adding them to the MIME mappings.
    326     for(Vector<String>::const_iterator it = mimeEntries.begin();
    327             it != mimeEntries.end(); ++it) {
    328         // Each part is split into 3 fields separated by colons
    329         // Field 1 is the MIME type (e.g "application/x-shockwave-flash").
    330         // Field 2 is a comma separated list of file extensions.
    331         // Field 3 is a human readable short description.
    332         const String &mimeEntry = *it;
    333         Vector<String> fields;
    334         mimeEntry.split(':', true, fields);
    335         if(fields.size() != 3) {
    336             PLUGIN_LOG("Bad MIME entry \"%s\"\n", mimeEntry.utf8().data());
    337             return false;
    338         }
    339 
    340         const String& mimeType = fields[0];
    341         Vector<String> extensions;
    342         fields[1].split(',', true, extensions);
    343         const String& description = fields[2];
    344 
    345         determineQuirks(mimeType);
    346 
    347         PLUGIN_LOG("mime_type: \"%s\"\n", mimeType.utf8().data());
    348         PLUGIN_LOG("extensions: \"%s\"\n", fields[1].utf8().data());
    349         PLUGIN_LOG("description: \"%s\"\n", description.utf8().data());
    350 
    351         // Map the mime type to the vector of extensions and the description
    352         if(!extensions.isEmpty())
    353             m_mimeToExtensions.set(mimeType, extensions);
    354         if(!description.isEmpty())
    355             m_mimeToDescriptions.set(mimeType, description);
    356     }
    357 
    358     PLUGIN_LOG("Fetch Info Loaded plugin details ok \"%s\"\n",
    359             m_path.utf8().data());
    360 
    361     // If this plugin needs to be kept in memory, unload the module now
    362     // and load it permanently.
    363     if (m_quirks.contains(PluginQuirkDontUnloadPlugin)) {
    364         dlCloser.ok();
    365         dlclose(handle);
    366         load();
    367     }
    368 
    369     // dlCloser will unload the plugin if required.
    370     return true;
    371 }
    372 
    373 unsigned PluginPackage::hash() const
    374 {
    375     const unsigned hashCodes[] = {
    376         m_name.impl()->hash(),
    377         m_description.impl()->hash(),
    378         m_mimeToExtensions.size(),
    379     };
    380 
    381     return StringImpl::computeHash(reinterpret_cast<const UChar*>(hashCodes),
    382                                    sizeof(hashCodes) / sizeof(UChar));
    383 }
    384 
    385 bool PluginPackage::equal(const PluginPackage& a, const PluginPackage& b)
    386 {
    387     if (a.m_name != b.m_name)
    388         return false;
    389 
    390     if (a.m_description != b.m_description)
    391         return false;
    392 
    393     if (a.m_mimeToExtensions.size() != b.m_mimeToExtensions.size())
    394         return false;
    395 
    396     MIMEToExtensionsMap::const_iterator::Keys end =
    397             a.m_mimeToExtensions.end().keys();
    398     for (MIMEToExtensionsMap::const_iterator::Keys it =
    399                  a.m_mimeToExtensions.begin().keys();
    400          it != end;
    401          ++it) {
    402         if (!b.m_mimeToExtensions.contains(*it)) {
    403             return false;
    404         }
    405     }
    406 
    407     return true;
    408 }
    409 
    410 } // namespace WebCore
    411 
    412 #endif
    413