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/metrics/stats_counters.h"
11 #include "base/strings/string_util.h"
12 #include "content/child/npapi/plugin_host.h"
13 #include "content/child/npapi/plugin_instance.h"
14 #include "content/common/plugin_list.h"
18 const char kPluginLibrariesLoadedCounter
[] = "PluginLibrariesLoaded";
19 const char kPluginInstancesActiveCounter
[] = "PluginInstancesActive";
21 // A list of all the instantiated plugins.
22 static std::vector
<scoped_refptr
<PluginLib
> >* g_loaded_libs
;
24 PluginLib
* PluginLib::CreatePluginLib(const base::FilePath
& filename
) {
25 // We can only have one PluginLib object per plugin as it controls the per
26 // instance function calls (i.e. NP_Initialize and NP_Shutdown). So we keep
27 // a map of PluginLib objects.
29 g_loaded_libs
= new std::vector
<scoped_refptr
<PluginLib
> >;
31 for (size_t i
= 0; i
< g_loaded_libs
->size(); ++i
) {
32 if ((*g_loaded_libs
)[i
]->plugin_info().path
== filename
)
33 return (*g_loaded_libs
)[i
].get();
37 if (!PluginList::Singleton()->ReadPluginInfo(filename
, &info
))
40 return new PluginLib(info
);
43 void PluginLib::UnloadAllPlugins() {
45 // PluginLib::Unload() can remove items from the list and even delete
46 // the list when it removes the last item, so we must work with a copy
47 // of the list so that we don't get the carpet removed under our feet.
48 std::vector
<scoped_refptr
<PluginLib
> > loaded_libs(*g_loaded_libs
);
49 for (size_t i
= 0; i
< loaded_libs
.size(); ++i
)
50 loaded_libs
[i
]->Unload();
52 if (g_loaded_libs
&& g_loaded_libs
->empty()) {
59 void PluginLib::ShutdownAllPlugins() {
61 for (size_t i
= 0; i
< g_loaded_libs
->size(); ++i
)
62 (*g_loaded_libs
)[i
]->Shutdown();
66 PluginLib::PluginLib(const WebPluginInfo
& info
)
67 : web_plugin_info_(info
),
73 defer_unload_(false) {
74 base::StatsCounter(kPluginLibrariesLoadedCounter
).Increment();
75 memset(static_cast<void*>(&plugin_funcs_
), 0, sizeof(plugin_funcs_
));
76 g_loaded_libs
->push_back(make_scoped_refptr(this));
78 memset(&entry_points_
, 0, sizeof(entry_points_
));
81 PluginLib::~PluginLib() {
82 base::StatsCounter(kPluginLibrariesLoadedCounter
).Decrement();
83 if (saved_data_
!= 0) {
84 // TODO - delete the savedData object here
88 NPPluginFuncs
* PluginLib::functions() {
89 return &plugin_funcs_
;
92 NPError
PluginLib::NP_Initialize() {
93 LOG_IF(ERROR
, PluginList::DebugPluginLoading())
94 << "PluginLib::NP_Initialize(" << web_plugin_info_
.path
.value()
95 << "): initialized=" << initialized_
;
97 return NPERR_NO_ERROR
;
100 return NPERR_MODULE_LOAD_FAILED_ERROR
;
102 PluginHost
* host
= PluginHost::Singleton();
104 return NPERR_GENERIC_ERROR
;
106 #if defined(OS_POSIX) && !defined(OS_MACOSX)
107 NPError rv
= entry_points_
.np_initialize(host
->host_functions(),
110 NPError rv
= entry_points_
.np_initialize(host
->host_functions());
111 #if defined(OS_MACOSX)
112 // On the Mac, we need to get entry points after calling np_initialize to
113 // match the behavior of other browsers.
114 if (rv
== NPERR_NO_ERROR
) {
115 rv
= entry_points_
.np_getentrypoints(&plugin_funcs_
);
119 LOG_IF(ERROR
, PluginList::DebugPluginLoading())
120 << "PluginLib::NP_Initialize(" << web_plugin_info_
.path
.value()
121 << "): result=" << rv
;
122 initialized_
= (rv
== NPERR_NO_ERROR
);
126 void PluginLib::NP_Shutdown(void) {
127 DCHECK(initialized_
);
128 entry_points_
.np_shutdown();
131 NPError
PluginLib::NP_ClearSiteData(const char* site
,
134 DCHECK(initialized_
);
135 if (plugin_funcs_
.clearsitedata
)
136 return plugin_funcs_
.clearsitedata(site
, flags
, max_age
);
137 return NPERR_INVALID_FUNCTABLE_ERROR
;
140 char** PluginLib::NP_GetSitesWithData() {
141 DCHECK(initialized_
);
142 if (plugin_funcs_
.getsiteswithdata
)
143 return plugin_funcs_
.getsiteswithdata();
147 void PluginLib::PreventLibraryUnload() {
151 PluginInstance
* PluginLib::CreateInstance(const std::string
& mime_type
) {
152 PluginInstance
* new_instance
= new PluginInstance(this, mime_type
);
154 base::StatsCounter(kPluginInstancesActiveCounter
).Increment();
155 DCHECK_NE(static_cast<PluginInstance
*>(NULL
), new_instance
);
159 void PluginLib::CloseInstance() {
160 base::StatsCounter(kPluginInstancesActiveCounter
).Decrement();
162 // If a plugin is running in its own process it will get unloaded on process
164 if ((instance_count_
== 0) && !defer_unload_
)
168 bool PluginLib::Load() {
173 base::NativeLibrary library
= 0;
174 base::NativeLibraryLoadError error
;
177 // This is to work around a bug in the Real player recorder plugin which
178 // intercepts LoadLibrary calls from chrome.dll and wraps NPAPI functions
179 // provided by the plugin. It crashes if the media player plugin is being
180 // loaded. Workaround is to load the dll dynamically by getting the
181 // LoadLibrary API address from kernel32.dll which bypasses the recorder
183 if (web_plugin_info_
.name
.find(L
"Windows Media Player") !=
184 std::wstring::npos
) {
185 library
= base::LoadNativeLibraryDynamically(web_plugin_info_
.path
);
187 library
= base::LoadNativeLibrary(web_plugin_info_
.path
, &error
);
190 library
= base::LoadNativeLibrary(web_plugin_info_
.path
, &error
);
194 LOG_IF(ERROR
, PluginList::DebugPluginLoading())
195 << "Couldn't load plugin " << web_plugin_info_
.path
.value() << " "
200 #if defined(OS_MACOSX)
201 // According to the WebKit source, QuickTime at least requires us to call
202 // UseResFile on the plugin resources before loading.
203 if (library
->bundle_resource_ref
!= -1)
204 UseResFile(library
->bundle_resource_ref
);
207 rv
= true; // assume success now
209 entry_points_
.np_initialize
=
210 (NP_InitializeFunc
)base::GetFunctionPointerFromNativeLibrary(library
,
212 if (entry_points_
.np_initialize
== 0)
215 #if defined(OS_WIN) || defined(OS_MACOSX)
216 entry_points_
.np_getentrypoints
=
217 (NP_GetEntryPointsFunc
)base::GetFunctionPointerFromNativeLibrary(
218 library
, "NP_GetEntryPoints");
219 if (entry_points_
.np_getentrypoints
== 0)
223 entry_points_
.np_shutdown
=
224 (NP_ShutdownFunc
)base::GetFunctionPointerFromNativeLibrary(library
,
226 if (entry_points_
.np_shutdown
== 0)
230 plugin_funcs_
.size
= sizeof(plugin_funcs_
);
231 plugin_funcs_
.version
= (NP_VERSION_MAJOR
<< 8) | NP_VERSION_MINOR
;
232 #if !defined(OS_POSIX)
233 if (entry_points_
.np_getentrypoints(&plugin_funcs_
) != NPERR_NO_ERROR
)
236 // On Linux and Mac, we get the plugin entry points during NP_Initialize.
241 LOG_IF(ERROR
, PluginList::DebugPluginLoading())
242 << "Plugin " << web_plugin_info_
.path
.value()
243 << " loaded successfully.";
246 LOG_IF(ERROR
, PluginList::DebugPluginLoading())
247 << "Plugin " << web_plugin_info_
.path
.value()
248 << " failed to load, unloading.";
249 base::UnloadNativeLibrary(library
);
255 // This is a helper to help perform a delayed NP_Shutdown and FreeLibrary on the
257 void FreePluginLibraryHelper(const base::FilePath
& path
,
258 base::NativeLibrary library
,
259 NP_ShutdownFunc shutdown_func
) {
261 // Don't call NP_Shutdown if the library has been reloaded since this task
263 bool reloaded
= false;
265 for (size_t i
= 0; i
< g_loaded_libs
->size(); ++i
) {
266 if ((*g_loaded_libs
)[i
]->plugin_info().path
== path
) {
277 // Always call base::UnloadNativeLibrary so that the system reference
278 // count is decremented.
279 base::UnloadNativeLibrary(library
);
283 void PluginLib::Unload() {
285 // In case of single process mode, a plugin can delete itself
286 // by executing a script. So delay the unloading of the library
287 // so that the plugin will have a chance to unwind.
288 /* TODO(dglazkov): Revisit when re-enabling the JSC build.
290 // The plugin NPAPI instances may still be around. Delay the
291 // NP_Shutdown and FreeLibrary calls at least till the next
296 if (!defer_unload_
) {
297 LOG_IF(ERROR
, PluginList::DebugPluginLoading())
298 << "Scheduling delayed unload for plugin "
299 << web_plugin_info_
.path
.value();
300 base::MessageLoop::current()->PostTask(
302 base::Bind(&FreePluginLibraryHelper
,
303 web_plugin_info_
.path
,
304 skip_unload_
? NULL
: library_
,
305 entry_points_
.np_shutdown
));
309 LOG_IF(ERROR
, PluginList::DebugPluginLoading())
310 << "Unloading plugin " << web_plugin_info_
.path
.value();
311 base::UnloadNativeLibrary(library_
);
318 for (size_t i
= 0; i
< g_loaded_libs
->size(); ++i
) {
319 if ((*g_loaded_libs
)[i
].get() == this) {
320 g_loaded_libs
->erase(g_loaded_libs
->begin() + i
);
324 if (g_loaded_libs
->empty()) {
325 delete g_loaded_libs
;
326 g_loaded_libs
= NULL
;
330 void PluginLib::Shutdown() {
333 initialized_
= false;
337 } // namespace content