diff options
author | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-12-20 21:38:12 +0000 |
---|---|---|
committer | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-12-20 21:38:12 +0000 |
commit | b692a128b073b297a4ce2aa7c468fdd37823e3a8 (patch) | |
tree | 3561224ba86e8eabd75f3e7a2f2343002d9a58ff /webkit/plugins/npapi/plugin_lib.cc | |
parent | b07c8d9a926a89d0aa7c0de2e1d2685b728a887e (diff) | |
download | chromium_src-b692a128b073b297a4ce2aa7c468fdd37823e3a8.zip chromium_src-b692a128b073b297a4ce2aa7c468fdd37823e3a8.tar.gz chromium_src-b692a128b073b297a4ce2aa7c468fdd37823e3a8.tar.bz2 |
Move the NPAPI files from webkit/glue/plugins to webkit/plugins/npapi and put
them in the webkit::npapi namespace.
BUG=none
TEST=none
Review URL: http://codereview.chromium.org/6012002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@69755 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit/plugins/npapi/plugin_lib.cc')
-rw-r--r-- | webkit/plugins/npapi/plugin_lib.cc | 351 |
1 files changed, 351 insertions, 0 deletions
diff --git a/webkit/plugins/npapi/plugin_lib.cc b/webkit/plugins/npapi/plugin_lib.cc new file mode 100644 index 0000000..d801ee0 --- /dev/null +++ b/webkit/plugins/npapi/plugin_lib.cc @@ -0,0 +1,351 @@ +// Copyright (c) 2010 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 "webkit/plugins/npapi/plugin_lib.h" + +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/metrics/stats_counters.h" +#include "base/string_util.h" +#include "webkit/glue/webkit_glue.h" +#include "webkit/plugins/npapi/plugin_instance.h" +#include "webkit/plugins/npapi/plugin_host.h" +#include "webkit/plugins/npapi/plugin_list.h" + +namespace webkit { +namespace npapi { + +const char kPluginLibrariesLoadedCounter[] = "PluginLibrariesLoaded"; +const char kPluginInstancesActiveCounter[] = "PluginInstancesActive"; + +// A list of all the instantiated plugins. +static std::vector<scoped_refptr<PluginLib> >* 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<scoped_refptr<PluginLib> >; + + 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; + const PluginEntryPoints* entry_points = NULL; + if (!PluginList::Singleton()->ReadPluginInfo(filename, &info, &entry_points)) + return NULL; + + return new PluginLib(info, entry_points); +} + +void PluginLib::UnloadAllPlugins() { + if (g_loaded_libs) { + // PluginLib::Unload() can remove items from the list and even delete + // the list when it removes the last item, so we must work with a copy + // of the list so that we don't get the carpet removed under our feet. + std::vector<scoped_refptr<PluginLib> > loaded_libs(*g_loaded_libs); + for (size_t i = 0; i < loaded_libs.size(); ++i) + loaded_libs[i]->Unload(); + + if (g_loaded_libs && g_loaded_libs->empty()) { + 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, + const PluginEntryPoints* entry_points) + : web_plugin_info_(info), + library_(NULL), + initialized_(false), + saved_data_(0), + instance_count_(0), + skip_unload_(false) { + base::StatsCounter(kPluginLibrariesLoadedCounter).Increment(); + memset(static_cast<void*>(&plugin_funcs_), 0, sizeof(plugin_funcs_)); + g_loaded_libs->push_back(make_scoped_refptr(this)); + + if (entry_points) { + internal_ = true; + entry_points_ = *entry_points; + } else { + internal_ = false; + // We will read the entry points from the plugin directly. + memset(&entry_points_, 0, sizeof(entry_points_)); + } +} + +PluginLib::~PluginLib() { + base::StatsCounter(kPluginLibrariesLoadedCounter).Decrement(); + if (saved_data_ != 0) { + // TODO - delete the savedData object here + } +} + +NPPluginFuncs* PluginLib::functions() { + return &plugin_funcs_; +} + +NPError PluginLib::NP_Initialize() { + LOG_IF(ERROR, PluginList::DebugPluginLoading()) + << "PluginLib::NP_Initialize(" << web_plugin_info_.path.value() + << "): initialized=" << initialized_; + 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; + +#if defined(OS_POSIX) && !defined(OS_MACOSX) + NPError rv = entry_points_.np_initialize(host->host_functions(), + &plugin_funcs_); +#else + NPError rv = entry_points_.np_initialize(host->host_functions()); +#if defined(OS_MACOSX) + // On the Mac, we need to get entry points after calling np_initialize to + // match the behavior of other browsers. + if (rv == NPERR_NO_ERROR) { + rv = entry_points_.np_getentrypoints(&plugin_funcs_); + } +#endif // OS_MACOSX +#endif + LOG_IF(ERROR, PluginList::DebugPluginLoading()) + << "PluginLib::NP_Initialize(" << web_plugin_info_.path.value() + << "): result=" << rv; + initialized_ = (rv == NPERR_NO_ERROR); + return rv; +} + +void PluginLib::NP_Shutdown(void) { + DCHECK(initialized_); + entry_points_.np_shutdown(); +} + +void PluginLib::PreventLibraryUnload() { + skip_unload_ = true; +} + +PluginInstance* PluginLib::CreateInstance(const std::string& mime_type) { + PluginInstance* new_instance = new PluginInstance(this, mime_type); + instance_count_++; + base::StatsCounter(kPluginInstancesActiveCounter).Increment(); + DCHECK_NE(static_cast<PluginInstance*>(NULL), new_instance); + return new_instance; +} + +void PluginLib::CloseInstance() { + base::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(); +} + +bool PluginLib::Load() { + if (library_) + return true; + + bool rv = false; + base::NativeLibrary library = 0; + + if (!internal_) { +#if defined(OS_WIN) + // This is to work around a bug in the Real player recorder plugin which + // intercepts LoadLibrary calls from chrome.dll and wraps NPAPI functions + // provided by the plugin. It crashes if the media player plugin is being + // loaded. Workaround is to load the dll dynamically by getting the + // LoadLibrary API address from kernel32.dll which bypasses the recorder + // plugin. + if (web_plugin_info_.name.find(L"Windows Media Player") != + std::wstring::npos) { + library = base::LoadNativeLibraryDynamically(web_plugin_info_.path); + } else { + library = base::LoadNativeLibrary(web_plugin_info_.path); + } +#else // OS_WIN + library = base::LoadNativeLibrary(web_plugin_info_.path); +#endif // OS_WIN + if (library == 0) { + LOG_IF(ERROR, PluginList::DebugPluginLoading()) + << "Couldn't load plugin " << web_plugin_info_.path.value(); + return rv; + } + +#if defined(OS_MACOSX) + // According to the WebKit source, QuickTime at least requires us to call + // UseResFile on the plugin resources before loading. + if (library->bundle_resource_ref != -1) + UseResFile(library->bundle_resource_ref); +#endif + + rv = true; // assume success now + + entry_points_.np_initialize = + (NP_InitializeFunc)base::GetFunctionPointerFromNativeLibrary(library, + "NP_Initialize"); + if (entry_points_.np_initialize == 0) + rv = false; + +#if defined(OS_WIN) || defined(OS_MACOSX) + entry_points_.np_getentrypoints = + (NP_GetEntryPointsFunc)base::GetFunctionPointerFromNativeLibrary( + library, "NP_GetEntryPoints"); + if (entry_points_.np_getentrypoints == 0) + rv = false; +#endif + + entry_points_.np_shutdown = + (NP_ShutdownFunc)base::GetFunctionPointerFromNativeLibrary(library, + "NP_Shutdown"); + if (entry_points_.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 !defined(OS_POSIX) + if (entry_points_.np_getentrypoints(&plugin_funcs_) != NPERR_NO_ERROR) + rv = false; +#else + // On Linux and Mac, we get the plugin entry points during NP_Initialize. +#endif + } + + if (!internal_) { + if (rv) { + LOG_IF(ERROR, PluginList::DebugPluginLoading()) + << "Plugin " << web_plugin_info_.path.value() + << " loaded successfully."; + library_ = library; + } else { + LOG_IF(ERROR, PluginList::DebugPluginLoading()) + << "Plugin " << web_plugin_info_.path.value() + << " failed to load, unloading."; + base::UnloadNativeLibrary(library); + } + } + + return rv; +} + +// This class implements delayed NP_Shutdown and FreeLibrary on the plugin dll. +class FreePluginLibraryTask : public Task { + public: + FreePluginLibraryTask(const FilePath& path, + base::NativeLibrary library, + NP_ShutdownFunc shutdown_func) + : path_(path), + library_(library), + NP_Shutdown_(shutdown_func) { + } + + ~FreePluginLibraryTask() {} + + void Run() { + if (NP_Shutdown_) { + // Don't call NP_Shutdown if the library has been reloaded since this task + // was posted. + bool reloaded = false; + if (g_loaded_libs) { + for (size_t i = 0; i < g_loaded_libs->size(); ++i) { + if ((*g_loaded_libs)[i]->plugin_info().path == path_) + reloaded = true; + } + } + if (!reloaded) + NP_Shutdown_(); + } + + if (library_) { + // Always call base::UnloadNativeLibrary so that the system reference + // count is decremented. + base::UnloadNativeLibrary(library_); + library_ = NULL; + } + } + + private: + FilePath path_; + base::NativeLibrary library_; + NP_ShutdownFunc NP_Shutdown_; + DISALLOW_COPY_AND_ASSIGN(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(); + +/* TODO(dglazkov): Revisit when re-enabling the JSC build. +#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(web_plugin_info_.path, + skip_unload_ ? NULL : library_, + entry_points_.np_shutdown); + LOG_IF(ERROR, PluginList::DebugPluginLoading()) + << "Scheduling delayed unload for plugin " + << web_plugin_info_.path.value(); + MessageLoop::current()->PostTask(FROM_HERE, free_library_task); + } else { + Shutdown(); + if (!skip_unload_) { + LOG_IF(ERROR, PluginList::DebugPluginLoading()) + << "Unloading plugin " << web_plugin_info_.path.value(); + base::UnloadNativeLibrary(library_); + } + } + + library_ = NULL; + } + + 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; + } +} + +void PluginLib::Shutdown() { + if (initialized_ && !internal_) { + NP_Shutdown(); + initialized_ = false; + } +} + +} // namespace npapi +} // namespace webkit |