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