// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "config.h" #include "webkit/glue/plugins/plugin_lib.h" #include "base/logging.h" #include "base/message_loop.h" #include "base/stats_counters.h" #include "base/string_util.h" #include "webkit/glue/webkit_glue.h" #include "webkit/glue/plugins/plugin_instance.h" #include "webkit/glue/plugins/plugin_host.h" namespace NPAPI { const char kPluginLibrariesLoadedCounter[] = "PluginLibrariesLoaded"; const char kPluginInstancesActiveCounter[] = "PluginInstancesActive"; // A list of all the instantiated plugins. static std::vector >* g_loaded_libs; PluginLib* PluginLib::CreatePluginLib(const FilePath& filename) { // We can only have one PluginLib object per plugin as it controls the per // instance function calls (i.e. NP_Initialize and NP_Shutdown). So we keep // a map of PluginLib objects. if (!g_loaded_libs) g_loaded_libs = new std::vector >; for (size_t i = 0; i < g_loaded_libs->size(); ++i) { if ((*g_loaded_libs)[i]->plugin_info().path == filename) return (*g_loaded_libs)[i]; } WebPluginInfo info; NP_GetEntryPointsFunc np_getentrypoints; NP_InitializeFunc np_initialize; NP_ShutdownFunc np_shutdown; if (!ReadWebPluginInfo(filename, &info, &np_getentrypoints, &np_initialize, &np_shutdown)) { return NULL; } return new PluginLib(info, np_getentrypoints, np_initialize, np_shutdown); } void PluginLib::UnloadAllPlugins() { if (g_loaded_libs) { for (size_t i = 0; i < g_loaded_libs->size(); ++i) (*g_loaded_libs)[i]->Unload(); delete g_loaded_libs; g_loaded_libs = NULL; } } void PluginLib::ShutdownAllPlugins() { if (g_loaded_libs) { for (size_t i = 0; i < g_loaded_libs->size(); ++i) (*g_loaded_libs)[i]->Shutdown(); } } PluginLib::PluginLib(const WebPluginInfo& info, NP_GetEntryPointsFunc np_getentrypoints, NP_InitializeFunc np_initialize, NP_ShutdownFunc np_shutdown) : web_plugin_info_(info), library_(0), initialized_(false), saved_data_(0), instance_count_(0) { StatsCounter(kPluginLibrariesLoadedCounter).Increment(); memset((void*)&plugin_funcs_, 0, sizeof(plugin_funcs_)); g_loaded_libs->push_back(this); if (np_getentrypoints && np_initialize && np_shutdown) { internal_ = true; NP_GetEntryPoints_ = np_getentrypoints; NP_Initialize_ = np_initialize; NP_Shutdown_ = np_shutdown; } else { internal_ = false; } } PluginLib::~PluginLib() { StatsCounter(kPluginLibrariesLoadedCounter).Decrement(); if (saved_data_ != 0) { // TODO - delete the savedData object here } } NPPluginFuncs* PluginLib::functions() { return &plugin_funcs_; } NPError PluginLib::NP_Initialize() { if (initialized_) return NPERR_NO_ERROR; if (!Load()) return NPERR_MODULE_LOAD_FAILED_ERROR; PluginHost* host = PluginHost::Singleton(); if (host == 0) return NPERR_GENERIC_ERROR; NPError rv = NP_Initialize_(host->host_functions()); initialized_ = (rv == NPERR_NO_ERROR); return rv; } void PluginLib::NP_Shutdown(void) { DCHECK(initialized_); NP_Shutdown_(); } PluginInstance* PluginLib::CreateInstance(const std::string& mime_type) { PluginInstance* new_instance = new PluginInstance(this, mime_type); instance_count_++; StatsCounter(kPluginInstancesActiveCounter).Increment(); DCHECK(new_instance != 0); return new_instance; } void PluginLib::CloseInstance() { StatsCounter(kPluginInstancesActiveCounter).Decrement(); instance_count_--; // If a plugin is running in its own process it will get unloaded on process // shutdown. if ((instance_count_ == 0) && webkit_glue::IsPluginRunningInRendererProcess()) { Unload(); for (size_t i = 0; i < g_loaded_libs->size(); ++i) { if ((*g_loaded_libs)[i].get() == this) { g_loaded_libs->erase(g_loaded_libs->begin() + i); break; } } if (g_loaded_libs->empty()) { delete g_loaded_libs; g_loaded_libs = NULL; } } } bool PluginLib::Load() { bool rv = false; NativeLibrary library = 0; if (!internal_) { if (library_ != 0) return rv; library = LoadNativeLibrary(web_plugin_info_.path); if (library == 0) return rv; rv = true; // assume success now NP_Initialize_ = (NP_InitializeFunc)GetFunctionPointerFromNativeLibrary( library, FUNCTION_NAME("NP_Initialize")); if (NP_Initialize_ == 0) rv = false; NP_GetEntryPoints_ = (NP_GetEntryPointsFunc)GetFunctionPointerFromNativeLibrary( library, FUNCTION_NAME("NP_GetEntryPoints")); if (NP_GetEntryPoints_ == 0) rv = false; NP_Shutdown_ = (NP_ShutdownFunc)GetFunctionPointerFromNativeLibrary( library, FUNCTION_NAME("NP_Shutdown")); if (NP_Shutdown_ == 0) rv = false; } else { rv = true; } if (rv) { plugin_funcs_.size = sizeof(plugin_funcs_); plugin_funcs_.version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR; if (NP_GetEntryPoints_(&plugin_funcs_) != NPERR_NO_ERROR) rv = false; } if (!internal_) { if (rv) library_ = library; else UnloadNativeLibrary(library); } return rv; } // This class implements delayed NP_Shutdown and FreeLibrary on the plugin dll. class FreePluginLibraryTask : public Task { public: FreePluginLibraryTask(PluginLib::NativeLibrary library, NP_ShutdownFunc shutdown_func) : library_(library), NP_Shutdown_(shutdown_func) { } ~FreePluginLibraryTask() {} void Run() { if (NP_Shutdown_) NP_Shutdown_(); if (library_) { PluginLib::UnloadNativeLibrary(library_); library_ = NULL; } } private: PluginLib::NativeLibrary library_; NP_ShutdownFunc NP_Shutdown_; DISALLOW_EVIL_CONSTRUCTORS(FreePluginLibraryTask); }; void PluginLib::Unload() { if (!internal_ && library_) { // In case of single process mode, a plugin can delete itself // by executing a script. So delay the unloading of the library // so that the plugin will have a chance to unwind. bool defer_unload = webkit_glue::IsPluginRunningInRendererProcess(); #if USE(JSC) // The plugin NPAPI instances may still be around. Delay the // NP_Shutdown and FreeLibrary calls at least till the next // peek message. defer_unload = true; #endif if (defer_unload) { FreePluginLibraryTask* free_library_task = new FreePluginLibraryTask(library_, NP_Shutdown_); MessageLoop::current()->PostTask(FROM_HERE, free_library_task); } else { Shutdown(); UnloadNativeLibrary(library_); } library_ = 0; } } void PluginLib::Shutdown() { if (initialized_ && !internal_) { NP_Shutdown(); initialized_ = false; } } // Creates WebPluginInfo structure based on read in or built in // PluginVersionInfo. /* static */ bool PluginLib::CreateWebPluginInfo(const PluginVersionInfo& pvi, WebPluginInfo* info, NP_GetEntryPointsFunc* np_getentrypoints, NP_InitializeFunc* np_initialize, NP_ShutdownFunc* np_shutdown) { std::vector mime_types, file_extensions; std::vector descriptions; SplitString(WideToUTF8(pvi.mime_types), '|', &mime_types); SplitString(WideToUTF8(pvi.file_extensions), '|', &file_extensions); SplitString(pvi.type_descriptions, '|', &descriptions); info->mime_types.clear(); if (mime_types.empty()) return false; info->name = pvi.product_name; info->desc = pvi.file_description; info->version = pvi.file_version; info->path = FilePath(pvi.path); for (size_t i = 0; i < mime_types.size(); ++i) { WebPluginMimeType mime_type; mime_type.mime_type = StringToLowerASCII(mime_types[i]); if (file_extensions.size() > i) SplitString(file_extensions[i], ',', &mime_type.file_extensions); if (descriptions.size() > i) { mime_type.description = descriptions[i]; // On Windows, the description likely has a list of file extensions // embedded in it (e.g. "SurfWriter file (*.swr)"). Remove an extension // list from the description if it is present. size_t ext = mime_type.description.find(L"(*"); if (ext != std::wstring::npos) { if (ext > 1 && mime_type.description[ext -1] == ' ') ext--; mime_type.description.erase(ext); } } info->mime_types.push_back(mime_type); } *np_getentrypoints = pvi.np_getentrypoints; *np_initialize = pvi.np_initialize; *np_shutdown = pvi.np_shutdown; return true; } } // namespace NPAPI