Home | History | Annotate | Download | only in npapi
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "content/child/npapi/plugin_lib.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/logging.h"
      9 #include "base/message_loop/message_loop.h"
     10 #include "base/metrics/stats_counters.h"
     11 #include "base/strings/string_util.h"
     12 #include "content/child/npapi/plugin_host.h"
     13 #include "content/child/npapi/plugin_instance.h"
     14 #include "content/common/plugin_list.h"
     15 
     16 namespace content {
     17 
     18 const char kPluginLibrariesLoadedCounter[] = "PluginLibrariesLoaded";
     19 const char kPluginInstancesActiveCounter[] = "PluginInstancesActive";
     20 
     21 // A list of all the instantiated plugins.
     22 static std::vector<scoped_refptr<PluginLib> >* g_loaded_libs;
     23 
     24 PluginLib* PluginLib::CreatePluginLib(const base::FilePath& filename) {
     25   // We can only have one PluginLib object per plugin as it controls the per
     26   // instance function calls (i.e. NP_Initialize and NP_Shutdown).  So we keep
     27   // a map of PluginLib objects.
     28   if (!g_loaded_libs)
     29     g_loaded_libs = new std::vector<scoped_refptr<PluginLib> >;
     30 
     31   for (size_t i = 0; i < g_loaded_libs->size(); ++i) {
     32     if ((*g_loaded_libs)[i]->plugin_info().path == filename)
     33       return (*g_loaded_libs)[i].get();
     34   }
     35 
     36   WebPluginInfo info;
     37   if (!PluginList::Singleton()->ReadPluginInfo(filename, &info))
     38     return NULL;
     39 
     40   return new PluginLib(info);
     41 }
     42 
     43 void PluginLib::UnloadAllPlugins() {
     44   if (g_loaded_libs) {
     45     // PluginLib::Unload() can remove items from the list and even delete
     46     // the list when it removes the last item, so we must work with a copy
     47     // of the list so that we don't get the carpet removed under our feet.
     48     std::vector<scoped_refptr<PluginLib> > loaded_libs(*g_loaded_libs);
     49     for (size_t i = 0; i < loaded_libs.size(); ++i)
     50       loaded_libs[i]->Unload();
     51 
     52     if (g_loaded_libs && g_loaded_libs->empty()) {
     53       delete g_loaded_libs;
     54       g_loaded_libs = NULL;
     55     }
     56   }
     57 }
     58 
     59 void PluginLib::ShutdownAllPlugins() {
     60   if (g_loaded_libs) {
     61     for (size_t i = 0; i < g_loaded_libs->size(); ++i)
     62       (*g_loaded_libs)[i]->Shutdown();
     63   }
     64 }
     65 
     66 PluginLib::PluginLib(const WebPluginInfo& info)
     67     : web_plugin_info_(info),
     68       library_(NULL),
     69       initialized_(false),
     70       saved_data_(0),
     71       instance_count_(0),
     72       skip_unload_(false),
     73       defer_unload_(false) {
     74   base::StatsCounter(kPluginLibrariesLoadedCounter).Increment();
     75   memset(static_cast<void*>(&plugin_funcs_), 0, sizeof(plugin_funcs_));
     76   g_loaded_libs->push_back(make_scoped_refptr(this));
     77 
     78   memset(&entry_points_, 0, sizeof(entry_points_));
     79 }
     80 
     81 PluginLib::~PluginLib() {
     82   base::StatsCounter(kPluginLibrariesLoadedCounter).Decrement();
     83   if (saved_data_ != 0) {
     84     // TODO - delete the savedData object here
     85   }
     86 }
     87 
     88 NPPluginFuncs* PluginLib::functions() {
     89   return &plugin_funcs_;
     90 }
     91 
     92 NPError PluginLib::NP_Initialize() {
     93   LOG_IF(ERROR, PluginList::DebugPluginLoading())
     94       << "PluginLib::NP_Initialize(" << web_plugin_info_.path.value()
     95       << "): initialized=" << initialized_;
     96   if (initialized_)
     97     return NPERR_NO_ERROR;
     98 
     99   if (!Load())
    100     return NPERR_MODULE_LOAD_FAILED_ERROR;
    101 
    102   PluginHost* host = PluginHost::Singleton();
    103   if (host == 0)
    104     return NPERR_GENERIC_ERROR;
    105 
    106 #if defined(OS_POSIX) && !defined(OS_MACOSX)
    107   NPError rv = entry_points_.np_initialize(host->host_functions(),
    108                                            &plugin_funcs_);
    109 #else
    110   NPError rv = entry_points_.np_initialize(host->host_functions());
    111 #if defined(OS_MACOSX)
    112   // On the Mac, we need to get entry points after calling np_initialize to
    113   // match the behavior of other browsers.
    114   if (rv == NPERR_NO_ERROR) {
    115     rv = entry_points_.np_getentrypoints(&plugin_funcs_);
    116   }
    117 #endif  // OS_MACOSX
    118 #endif
    119   LOG_IF(ERROR, PluginList::DebugPluginLoading())
    120       << "PluginLib::NP_Initialize(" << web_plugin_info_.path.value()
    121       << "): result=" << rv;
    122   initialized_ = (rv == NPERR_NO_ERROR);
    123   return rv;
    124 }
    125 
    126 void PluginLib::NP_Shutdown(void) {
    127   DCHECK(initialized_);
    128   entry_points_.np_shutdown();
    129 }
    130 
    131 NPError PluginLib::NP_ClearSiteData(const char* site,
    132                                     uint64 flags,
    133                                     uint64 max_age) {
    134   DCHECK(initialized_);
    135   if (plugin_funcs_.clearsitedata)
    136     return plugin_funcs_.clearsitedata(site, flags, max_age);
    137   return NPERR_INVALID_FUNCTABLE_ERROR;
    138 }
    139 
    140 char** PluginLib::NP_GetSitesWithData() {
    141   DCHECK(initialized_);
    142   if (plugin_funcs_.getsiteswithdata)
    143     return plugin_funcs_.getsiteswithdata();
    144   return NULL;
    145 }
    146 
    147 void PluginLib::PreventLibraryUnload() {
    148   skip_unload_ = true;
    149 }
    150 
    151 PluginInstance* PluginLib::CreateInstance(const std::string& mime_type) {
    152   PluginInstance* new_instance = new PluginInstance(this, mime_type);
    153   instance_count_++;
    154   base::StatsCounter(kPluginInstancesActiveCounter).Increment();
    155   DCHECK_NE(static_cast<PluginInstance*>(NULL), new_instance);
    156   return new_instance;
    157 }
    158 
    159 void PluginLib::CloseInstance() {
    160   base::StatsCounter(kPluginInstancesActiveCounter).Decrement();
    161   instance_count_--;
    162   // If a plugin is running in its own process it will get unloaded on process
    163   // shutdown.
    164   if ((instance_count_ == 0) && !defer_unload_)
    165     Unload();
    166 }
    167 
    168 bool PluginLib::Load() {
    169   if (library_)
    170     return true;
    171 
    172   bool rv = false;
    173   base::NativeLibrary library = 0;
    174   base::NativeLibraryLoadError error;
    175 
    176 #if defined(OS_WIN)
    177   // This is to work around a bug in the Real player recorder plugin which
    178   // intercepts LoadLibrary calls from chrome.dll and wraps NPAPI functions
    179   // provided by the plugin. It crashes if the media player plugin is being
    180   // loaded. Workaround is to load the dll dynamically by getting the
    181   // LoadLibrary API address from kernel32.dll which bypasses the recorder
    182   // plugin.
    183   if (web_plugin_info_.name.find(L"Windows Media Player") !=
    184       std::wstring::npos) {
    185     library = base::LoadNativeLibraryDynamically(web_plugin_info_.path);
    186   } else {
    187     library = base::LoadNativeLibrary(web_plugin_info_.path, &error);
    188   }
    189 #else
    190   library = base::LoadNativeLibrary(web_plugin_info_.path, &error);
    191 #endif
    192 
    193   if (!library) {
    194     LOG_IF(ERROR, PluginList::DebugPluginLoading())
    195         << "Couldn't load plugin " << web_plugin_info_.path.value() << " "
    196         << error.ToString();
    197     return rv;
    198   }
    199 
    200 #if defined(OS_MACOSX)
    201   // According to the WebKit source, QuickTime at least requires us to call
    202   // UseResFile on the plugin resources before loading.
    203   if (library->bundle_resource_ref != -1)
    204     UseResFile(library->bundle_resource_ref);
    205 #endif
    206 
    207   rv = true;  // assume success now
    208 
    209   entry_points_.np_initialize =
    210       (NP_InitializeFunc)base::GetFunctionPointerFromNativeLibrary(library,
    211           "NP_Initialize");
    212   if (entry_points_.np_initialize == 0)
    213     rv = false;
    214 
    215 #if defined(OS_WIN) || defined(OS_MACOSX)
    216   entry_points_.np_getentrypoints =
    217       (NP_GetEntryPointsFunc)base::GetFunctionPointerFromNativeLibrary(
    218           library, "NP_GetEntryPoints");
    219   if (entry_points_.np_getentrypoints == 0)
    220     rv = false;
    221 #endif
    222 
    223   entry_points_.np_shutdown =
    224       (NP_ShutdownFunc)base::GetFunctionPointerFromNativeLibrary(library,
    225           "NP_Shutdown");
    226   if (entry_points_.np_shutdown == 0)
    227     rv = false;
    228 
    229   if (rv) {
    230     plugin_funcs_.size = sizeof(plugin_funcs_);
    231     plugin_funcs_.version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
    232 #if !defined(OS_POSIX)
    233     if (entry_points_.np_getentrypoints(&plugin_funcs_) != NPERR_NO_ERROR)
    234       rv = false;
    235 #else
    236     // On Linux and Mac, we get the plugin entry points during NP_Initialize.
    237 #endif
    238   }
    239 
    240   if (rv) {
    241     LOG_IF(ERROR, PluginList::DebugPluginLoading())
    242         << "Plugin " << web_plugin_info_.path.value()
    243         << " loaded successfully.";
    244     library_ = library;
    245   } else {
    246     LOG_IF(ERROR, PluginList::DebugPluginLoading())
    247         << "Plugin " << web_plugin_info_.path.value()
    248         << " failed to load, unloading.";
    249     base::UnloadNativeLibrary(library);
    250   }
    251 
    252   return rv;
    253 }
    254 
    255 // This is a helper to help perform a delayed NP_Shutdown and FreeLibrary on the
    256 // plugin dll.
    257 void FreePluginLibraryHelper(const base::FilePath& path,
    258                              base::NativeLibrary library,
    259                              NP_ShutdownFunc shutdown_func) {
    260   if (shutdown_func) {
    261     // Don't call NP_Shutdown if the library has been reloaded since this task
    262     // was posted.
    263     bool reloaded = false;
    264     if (g_loaded_libs) {
    265       for (size_t i = 0; i < g_loaded_libs->size(); ++i) {
    266         if ((*g_loaded_libs)[i]->plugin_info().path == path) {
    267           reloaded = true;
    268           break;
    269         }
    270       }
    271     }
    272     if (!reloaded)
    273       shutdown_func();
    274   }
    275 
    276   if (library) {
    277     // Always call base::UnloadNativeLibrary so that the system reference
    278     // count is decremented.
    279     base::UnloadNativeLibrary(library);
    280   }
    281 }
    282 
    283 void PluginLib::Unload() {
    284   if (library_) {
    285     // In case of single process mode, a plugin can delete itself
    286     // by executing a script. So delay the unloading of the library
    287     // so that the plugin will have a chance to unwind.
    288 /* TODO(dglazkov): Revisit when re-enabling the JSC build.
    289 #if USE(JSC)
    290     // The plugin NPAPI instances may still be around. Delay the
    291     // NP_Shutdown and FreeLibrary calls at least till the next
    292     // peek message.
    293     defer_unload = true;
    294 #endif
    295 */
    296     if (!defer_unload_) {
    297       LOG_IF(ERROR, PluginList::DebugPluginLoading())
    298           << "Scheduling delayed unload for plugin "
    299           << web_plugin_info_.path.value();
    300       base::MessageLoop::current()->PostTask(
    301           FROM_HERE,
    302           base::Bind(&FreePluginLibraryHelper,
    303                      web_plugin_info_.path,
    304                      skip_unload_ ? NULL : library_,
    305                      entry_points_.np_shutdown));
    306     } else {
    307       Shutdown();
    308       if (!skip_unload_) {
    309         LOG_IF(ERROR, PluginList::DebugPluginLoading())
    310             << "Unloading plugin " << web_plugin_info_.path.value();
    311         base::UnloadNativeLibrary(library_);
    312       }
    313     }
    314 
    315     library_ = NULL;
    316   }
    317 
    318   for (size_t i = 0; i < g_loaded_libs->size(); ++i) {
    319     if ((*g_loaded_libs)[i].get() == this) {
    320       g_loaded_libs->erase(g_loaded_libs->begin() + i);
    321       break;
    322     }
    323   }
    324   if (g_loaded_libs->empty()) {
    325     delete g_loaded_libs;
    326     g_loaded_libs = NULL;
    327   }
    328 }
    329 
    330 void PluginLib::Shutdown() {
    331   if (initialized_) {
    332     NP_Shutdown();
    333     initialized_ = false;
    334   }
    335 }
    336 
    337 }  // namespace content
    338