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"
8 #include "base/logging.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/strings/string_util.h"
11 #include "content/child/npapi/plugin_host.h"
12 #include "content/child/npapi/plugin_instance.h"
13 #include "content/common/plugin_list.h"
17 // A list of all the instantiated plugins.
18 static std::vector
<scoped_refptr
<PluginLib
> >* g_loaded_libs
;
20 PluginLib
* PluginLib::CreatePluginLib(const base::FilePath
& filename
) {
21 // We can only have one PluginLib object per plugin as it controls the per
22 // instance function calls (i.e. NP_Initialize and NP_Shutdown). So we keep
23 // a map of PluginLib objects.
25 g_loaded_libs
= new std::vector
<scoped_refptr
<PluginLib
> >;
27 for (size_t i
= 0; i
< g_loaded_libs
->size(); ++i
) {
28 if ((*g_loaded_libs
)[i
]->plugin_info().path
== filename
)
29 return (*g_loaded_libs
)[i
].get();
33 if (!PluginList::Singleton()->ReadPluginInfo(filename
, &info
))
36 return new PluginLib(info
);
39 void PluginLib::UnloadAllPlugins() {
41 // PluginLib::Unload() can remove items from the list and even delete
42 // the list when it removes the last item, so we must work with a copy
43 // of the list so that we don't get the carpet removed under our feet.
44 std::vector
<scoped_refptr
<PluginLib
> > loaded_libs(*g_loaded_libs
);
45 for (size_t i
= 0; i
< loaded_libs
.size(); ++i
)
46 loaded_libs
[i
]->Unload();
48 if (g_loaded_libs
&& g_loaded_libs
->empty()) {
55 void PluginLib::ShutdownAllPlugins() {
57 for (size_t i
= 0; i
< g_loaded_libs
->size(); ++i
)
58 (*g_loaded_libs
)[i
]->Shutdown();
62 PluginLib::PluginLib(const WebPluginInfo
& info
)
63 : web_plugin_info_(info
),
69 defer_unload_(false) {
70 memset(static_cast<void*>(&plugin_funcs_
), 0, sizeof(plugin_funcs_
));
71 g_loaded_libs
->push_back(make_scoped_refptr(this));
73 memset(&entry_points_
, 0, sizeof(entry_points_
));
76 PluginLib::~PluginLib() {
77 if (saved_data_
!= 0) {
78 // TODO - delete the savedData object here
82 NPPluginFuncs
* PluginLib::functions() {
83 return &plugin_funcs_
;
86 NPError
PluginLib::NP_Initialize() {
87 LOG_IF(ERROR
, PluginList::DebugPluginLoading())
88 << "PluginLib::NP_Initialize(" << web_plugin_info_
.path
.value()
89 << "): initialized=" << initialized_
;
91 return NPERR_NO_ERROR
;
94 return NPERR_MODULE_LOAD_FAILED_ERROR
;
96 PluginHost
* host
= PluginHost::Singleton();
98 return NPERR_GENERIC_ERROR
;
100 #if defined(OS_POSIX) && !defined(OS_MACOSX)
101 NPError rv
= entry_points_
.np_initialize(host
->host_functions(),
104 NPError rv
= entry_points_
.np_initialize(host
->host_functions());
105 #if defined(OS_MACOSX)
106 // On the Mac, we need to get entry points after calling np_initialize to
107 // match the behavior of other browsers.
108 if (rv
== NPERR_NO_ERROR
) {
109 rv
= entry_points_
.np_getentrypoints(&plugin_funcs_
);
113 LOG_IF(ERROR
, PluginList::DebugPluginLoading())
114 << "PluginLib::NP_Initialize(" << web_plugin_info_
.path
.value()
115 << "): result=" << rv
;
116 initialized_
= (rv
== NPERR_NO_ERROR
);
120 void PluginLib::NP_Shutdown(void) {
121 DCHECK(initialized_
);
122 entry_points_
.np_shutdown();
125 NPError
PluginLib::NP_ClearSiteData(const char* site
,
128 DCHECK(initialized_
);
129 if (plugin_funcs_
.clearsitedata
)
130 return plugin_funcs_
.clearsitedata(site
, flags
, max_age
);
131 return NPERR_INVALID_FUNCTABLE_ERROR
;
134 char** PluginLib::NP_GetSitesWithData() {
135 DCHECK(initialized_
);
136 if (plugin_funcs_
.getsiteswithdata
)
137 return plugin_funcs_
.getsiteswithdata();
141 void PluginLib::PreventLibraryUnload() {
145 PluginInstance
* PluginLib::CreateInstance(const std::string
& mime_type
) {
146 PluginInstance
* new_instance
= new PluginInstance(this, mime_type
);
148 DCHECK_NE(static_cast<PluginInstance
*>(NULL
), new_instance
);
152 void PluginLib::CloseInstance() {
154 // If a plugin is running in its own process it will get unloaded on process
156 if ((instance_count_
== 0) && !defer_unload_
)
160 bool PluginLib::Load() {
165 base::NativeLibrary library
= 0;
166 base::NativeLibraryLoadError error
;
169 // This is to work around a bug in the Real player recorder plugin which
170 // intercepts LoadLibrary calls from chrome.dll and wraps NPAPI functions
171 // provided by the plugin. It crashes if the media player plugin is being
172 // loaded. Workaround is to load the dll dynamically by getting the
173 // LoadLibrary API address from kernel32.dll which bypasses the recorder
175 if (web_plugin_info_
.name
.find(L
"Windows Media Player") !=
176 std::wstring::npos
) {
177 library
= base::LoadNativeLibraryDynamically(web_plugin_info_
.path
);
179 library
= base::LoadNativeLibrary(web_plugin_info_
.path
, &error
);
182 library
= base::LoadNativeLibrary(web_plugin_info_
.path
, &error
);
186 LOG_IF(ERROR
, PluginList::DebugPluginLoading())
187 << "Couldn't load plugin " << web_plugin_info_
.path
.value() << " "
192 #if defined(OS_MACOSX)
193 // According to the WebKit source, QuickTime at least requires us to call
194 // UseResFile on the plugin resources before loading.
195 if (library
->bundle_resource_ref
!= -1)
196 UseResFile(library
->bundle_resource_ref
);
199 rv
= true; // assume success now
201 entry_points_
.np_initialize
=
202 (NP_InitializeFunc
)base::GetFunctionPointerFromNativeLibrary(library
,
204 if (entry_points_
.np_initialize
== 0)
207 #if defined(OS_WIN) || defined(OS_MACOSX)
208 entry_points_
.np_getentrypoints
=
209 (NP_GetEntryPointsFunc
)base::GetFunctionPointerFromNativeLibrary(
210 library
, "NP_GetEntryPoints");
211 if (entry_points_
.np_getentrypoints
== 0)
215 entry_points_
.np_shutdown
=
216 (NP_ShutdownFunc
)base::GetFunctionPointerFromNativeLibrary(library
,
218 if (entry_points_
.np_shutdown
== 0)
222 plugin_funcs_
.size
= sizeof(plugin_funcs_
);
223 plugin_funcs_
.version
= (NP_VERSION_MAJOR
<< 8) | NP_VERSION_MINOR
;
224 #if !defined(OS_POSIX)
225 if (entry_points_
.np_getentrypoints(&plugin_funcs_
) != NPERR_NO_ERROR
)
228 // On Linux and Mac, we get the plugin entry points during NP_Initialize.
233 LOG_IF(ERROR
, PluginList::DebugPluginLoading())
234 << "Plugin " << web_plugin_info_
.path
.value()
235 << " loaded successfully.";
238 LOG_IF(ERROR
, PluginList::DebugPluginLoading())
239 << "Plugin " << web_plugin_info_
.path
.value()
240 << " failed to load, unloading.";
241 base::UnloadNativeLibrary(library
);
247 // This is a helper to help perform a delayed NP_Shutdown and FreeLibrary on the
249 void FreePluginLibraryHelper(const base::FilePath
& path
,
250 base::NativeLibrary library
,
251 NP_ShutdownFunc shutdown_func
) {
253 // Don't call NP_Shutdown if the library has been reloaded since this task
255 bool reloaded
= false;
257 for (size_t i
= 0; i
< g_loaded_libs
->size(); ++i
) {
258 if ((*g_loaded_libs
)[i
]->plugin_info().path
== path
) {
269 // Always call base::UnloadNativeLibrary so that the system reference
270 // count is decremented.
271 base::UnloadNativeLibrary(library
);
275 void PluginLib::Unload() {
277 // In case of single process mode, a plugin can delete itself
278 // by executing a script. So delay the unloading of the library
279 // so that the plugin will have a chance to unwind.
280 /* TODO(dglazkov): Revisit when re-enabling the JSC build.
282 // The plugin NPAPI instances may still be around. Delay the
283 // NP_Shutdown and FreeLibrary calls at least till the next
288 if (!defer_unload_
) {
289 LOG_IF(ERROR
, PluginList::DebugPluginLoading())
290 << "Scheduling delayed unload for plugin "
291 << web_plugin_info_
.path
.value();
292 base::MessageLoop::current()->PostTask(
294 base::Bind(&FreePluginLibraryHelper
,
295 web_plugin_info_
.path
,
296 skip_unload_
? NULL
: library_
,
297 entry_points_
.np_shutdown
));
301 LOG_IF(ERROR
, PluginList::DebugPluginLoading())
302 << "Unloading plugin " << web_plugin_info_
.path
.value();
303 base::UnloadNativeLibrary(library_
);
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
);
316 if (g_loaded_libs
->empty()) {
317 delete g_loaded_libs
;
318 g_loaded_libs
= NULL
;
322 void PluginLib::Shutdown() {
325 initialized_
= false;
329 } // namespace content