summaryrefslogtreecommitdiffstats
path: root/webkit/plugins/npapi/plugin_lib.cc
diff options
context:
space:
mode:
authorbrettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-12-20 21:38:12 +0000
committerbrettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-12-20 21:38:12 +0000
commitb692a128b073b297a4ce2aa7c468fdd37823e3a8 (patch)
tree3561224ba86e8eabd75f3e7a2f2343002d9a58ff /webkit/plugins/npapi/plugin_lib.cc
parentb07c8d9a926a89d0aa7c0de2e1d2685b728a887e (diff)
downloadchromium_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.cc351
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