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/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"
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.
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();
35 if (!PluginList::Singleton()->ReadPluginInfo(filename
, &info
))
38 return new PluginLib(info
);
41 void PluginLib::UnloadAllPlugins() {
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()) {
57 void PluginLib::ShutdownAllPlugins() {
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
),
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_
;
93 return NPERR_NO_ERROR
;
96 return NPERR_MODULE_LOAD_FAILED_ERROR
;
98 PluginHost
* host
= PluginHost::Singleton();
100 return NPERR_GENERIC_ERROR
;
102 #if defined(OS_POSIX) && !defined(OS_MACOSX)
103 NPError rv
= entry_points_
.np_initialize(host
->host_functions(),
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_
);
115 LOG_IF(ERROR
, PluginList::DebugPluginLoading())
116 << "PluginLib::NP_Initialize(" << web_plugin_info_
.path
.value()
117 << "): result=" << rv
;
118 initialized_
= (rv
== NPERR_NO_ERROR
);
122 void PluginLib::NP_Shutdown(void) {
123 DCHECK(initialized_
);
124 entry_points_
.np_shutdown();
127 NPError
PluginLib::NP_ClearSiteData(const char* site
,
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();
143 void PluginLib::PreventLibraryUnload() {
147 PluginInstance
* PluginLib::CreateInstance(const std::string
& mime_type
) {
148 PluginInstance
* new_instance
= new PluginInstance(this, mime_type
);
150 DCHECK_NE(static_cast<PluginInstance
*>(NULL
), new_instance
);
154 void PluginLib::CloseInstance() {
156 // If a plugin is running in its own process it will get unloaded on process
158 if ((instance_count_
== 0) && !defer_unload_
)
162 bool PluginLib::Load() {
167 base::NativeLibrary library
= 0;
168 base::NativeLibraryLoadError error
;
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
177 if (web_plugin_info_
.name
.find(L
"Windows Media Player") !=
178 std::wstring::npos
) {
179 library
= base::LoadNativeLibraryDynamically(web_plugin_info_
.path
);
181 library
= base::LoadNativeLibrary(web_plugin_info_
.path
, &error
);
184 library
= base::LoadNativeLibrary(web_plugin_info_
.path
, &error
);
188 LOG_IF(ERROR
, PluginList::DebugPluginLoading())
189 << "Couldn't load plugin " << web_plugin_info_
.path
.value() << " "
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
);
201 rv
= true; // assume success now
203 entry_points_
.np_initialize
=
204 (NP_InitializeFunc
)base::GetFunctionPointerFromNativeLibrary(library
,
206 if (entry_points_
.np_initialize
== 0)
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)
217 entry_points_
.np_shutdown
=
218 (NP_ShutdownFunc
)base::GetFunctionPointerFromNativeLibrary(library
,
220 if (entry_points_
.np_shutdown
== 0)
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
)
230 // On Linux and Mac, we get the plugin entry points during NP_Initialize.
235 LOG_IF(ERROR
, PluginList::DebugPluginLoading())
236 << "Plugin " << web_plugin_info_
.path
.value()
237 << " loaded successfully.";
240 LOG_IF(ERROR
, PluginList::DebugPluginLoading())
241 << "Plugin " << web_plugin_info_
.path
.value()
242 << " failed to load, unloading.";
243 base::UnloadNativeLibrary(library
);
249 // This is a helper to help perform a delayed NP_Shutdown and FreeLibrary on the
251 void FreePluginLibraryHelper(const base::FilePath
& path
,
252 base::NativeLibrary library
,
253 NP_ShutdownFunc shutdown_func
) {
255 // Don't call NP_Shutdown if the library has been reloaded since this task
257 bool reloaded
= false;
259 for (size_t i
= 0; i
< g_loaded_libs
->size(); ++i
) {
260 if ((*g_loaded_libs
)[i
]->plugin_info().path
== path
) {
271 // Always call base::UnloadNativeLibrary so that the system reference
272 // count is decremented.
273 base::UnloadNativeLibrary(library
);
277 void PluginLib::Unload() {
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.
284 // The plugin NPAPI instances may still be around. Delay the
285 // NP_Shutdown and FreeLibrary calls at least till the next
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
));
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