Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / content / child / npapi / plugin_lib.cc
blob9df2f589bb7a741b2c440f3e8573a99b9770d61c
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/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"
16 namespace content {
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.
28 if (!g_loaded_libs)
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();
36 WebPluginInfo info;
37 if (!PluginList::Singleton()->ReadPluginInfo(filename, &info))
38 return NULL;
40 return new PluginLib(info);
43 void PluginLib::UnloadAllPlugins() {
44 if (g_loaded_libs) {
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()) {
53 delete g_loaded_libs;
54 g_loaded_libs = NULL;
59 void PluginLib::ShutdownAllPlugins() {
60 if (g_loaded_libs) {
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),
68 library_(NULL),
69 initialized_(false),
70 saved_data_(0),
71 instance_count_(0),
72 skip_unload_(false),
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_;
96 if (initialized_)
97 return NPERR_NO_ERROR;
99 if (!Load())
100 return NPERR_MODULE_LOAD_FAILED_ERROR;
102 PluginHost* host = PluginHost::Singleton();
103 if (host == 0)
104 return NPERR_GENERIC_ERROR;
106 #if defined(OS_POSIX) && !defined(OS_MACOSX)
107 NPError rv = entry_points_.np_initialize(host->host_functions(),
108 &plugin_funcs_);
109 #else
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_);
117 #endif // OS_MACOSX
118 #endif
119 LOG_IF(ERROR, PluginList::DebugPluginLoading())
120 << "PluginLib::NP_Initialize(" << web_plugin_info_.path.value()
121 << "): result=" << rv;
122 initialized_ = (rv == NPERR_NO_ERROR);
123 return rv;
126 void PluginLib::NP_Shutdown(void) {
127 DCHECK(initialized_);
128 entry_points_.np_shutdown();
131 NPError PluginLib::NP_ClearSiteData(const char* site,
132 uint64 flags,
133 uint64 max_age) {
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();
144 return NULL;
147 void PluginLib::PreventLibraryUnload() {
148 skip_unload_ = true;
151 PluginInstance* PluginLib::CreateInstance(const std::string& mime_type) {
152 PluginInstance* new_instance = new PluginInstance(this, mime_type);
153 instance_count_++;
154 base::StatsCounter(kPluginInstancesActiveCounter).Increment();
155 DCHECK_NE(static_cast<PluginInstance*>(NULL), new_instance);
156 return new_instance;
159 void PluginLib::CloseInstance() {
160 base::StatsCounter(kPluginInstancesActiveCounter).Decrement();
161 instance_count_--;
162 // If a plugin is running in its own process it will get unloaded on process
163 // shutdown.
164 if ((instance_count_ == 0) && !defer_unload_)
165 Unload();
168 bool PluginLib::Load() {
169 if (library_)
170 return true;
172 bool rv = false;
173 base::NativeLibrary library = 0;
174 base::NativeLibraryLoadError error;
176 #if defined(OS_WIN)
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
182 // plugin.
183 if (web_plugin_info_.name.find(L"Windows Media Player") !=
184 std::wstring::npos) {
185 library = base::LoadNativeLibraryDynamically(web_plugin_info_.path);
186 } else {
187 library = base::LoadNativeLibrary(web_plugin_info_.path, &error);
189 #else
190 library = base::LoadNativeLibrary(web_plugin_info_.path, &error);
191 #endif
193 if (!library) {
194 LOG_IF(ERROR, PluginList::DebugPluginLoading())
195 << "Couldn't load plugin " << web_plugin_info_.path.value() << " "
196 << error.ToString();
197 return rv;
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);
205 #endif
207 rv = true; // assume success now
209 entry_points_.np_initialize =
210 (NP_InitializeFunc)base::GetFunctionPointerFromNativeLibrary(library,
211 "NP_Initialize");
212 if (entry_points_.np_initialize == 0)
213 rv = false;
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)
220 rv = false;
221 #endif
223 entry_points_.np_shutdown =
224 (NP_ShutdownFunc)base::GetFunctionPointerFromNativeLibrary(library,
225 "NP_Shutdown");
226 if (entry_points_.np_shutdown == 0)
227 rv = false;
229 if (rv) {
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)
234 rv = false;
235 #else
236 // On Linux and Mac, we get the plugin entry points during NP_Initialize.
237 #endif
240 if (rv) {
241 LOG_IF(ERROR, PluginList::DebugPluginLoading())
242 << "Plugin " << web_plugin_info_.path.value()
243 << " loaded successfully.";
244 library_ = library;
245 } else {
246 LOG_IF(ERROR, PluginList::DebugPluginLoading())
247 << "Plugin " << web_plugin_info_.path.value()
248 << " failed to load, unloading.";
249 base::UnloadNativeLibrary(library);
252 return rv;
255 // This is a helper to help perform a delayed NP_Shutdown and FreeLibrary on the
256 // plugin dll.
257 void FreePluginLibraryHelper(const base::FilePath& path,
258 base::NativeLibrary library,
259 NP_ShutdownFunc shutdown_func) {
260 if (shutdown_func) {
261 // Don't call NP_Shutdown if the library has been reloaded since this task
262 // was posted.
263 bool reloaded = false;
264 if (g_loaded_libs) {
265 for (size_t i = 0; i < g_loaded_libs->size(); ++i) {
266 if ((*g_loaded_libs)[i]->plugin_info().path == path) {
267 reloaded = true;
268 break;
272 if (!reloaded)
273 shutdown_func();
276 if (library) {
277 // Always call base::UnloadNativeLibrary so that the system reference
278 // count is decremented.
279 base::UnloadNativeLibrary(library);
283 void PluginLib::Unload() {
284 if (library_) {
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.
289 #if USE(JSC)
290 // The plugin NPAPI instances may still be around. Delay the
291 // NP_Shutdown and FreeLibrary calls at least till the next
292 // peek message.
293 defer_unload = true;
294 #endif
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(
301 FROM_HERE,
302 base::Bind(&FreePluginLibraryHelper,
303 web_plugin_info_.path,
304 skip_unload_ ? NULL : library_,
305 entry_points_.np_shutdown));
306 } else {
307 Shutdown();
308 if (!skip_unload_) {
309 LOG_IF(ERROR, PluginList::DebugPluginLoading())
310 << "Unloading plugin " << web_plugin_info_.path.value();
311 base::UnloadNativeLibrary(library_);
315 library_ = NULL;
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);
321 break;
324 if (g_loaded_libs->empty()) {
325 delete g_loaded_libs;
326 g_loaded_libs = NULL;
330 void PluginLib::Shutdown() {
331 if (initialized_) {
332 NP_Shutdown();
333 initialized_ = false;
337 } // namespace content