Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / child / npapi / plugin_lib.cc
blob1d7ebe957664cfb204e01726fef06cf03a4928e0
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/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"
15 namespace content {
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.
24 if (!g_loaded_libs)
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();
32 WebPluginInfo info;
33 if (!PluginList::Singleton()->ReadPluginInfo(filename, &info))
34 return NULL;
36 return new PluginLib(info);
39 void PluginLib::UnloadAllPlugins() {
40 if (g_loaded_libs) {
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()) {
49 delete g_loaded_libs;
50 g_loaded_libs = NULL;
55 void PluginLib::ShutdownAllPlugins() {
56 if (g_loaded_libs) {
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),
64 library_(NULL),
65 initialized_(false),
66 saved_data_(0),
67 instance_count_(0),
68 skip_unload_(false),
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_;
90 if (initialized_)
91 return NPERR_NO_ERROR;
93 if (!Load())
94 return NPERR_MODULE_LOAD_FAILED_ERROR;
96 PluginHost* host = PluginHost::Singleton();
97 if (host == 0)
98 return NPERR_GENERIC_ERROR;
100 #if defined(OS_POSIX) && !defined(OS_MACOSX)
101 NPError rv = entry_points_.np_initialize(host->host_functions(),
102 &plugin_funcs_);
103 #else
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_);
111 #endif // OS_MACOSX
112 #endif
113 LOG_IF(ERROR, PluginList::DebugPluginLoading())
114 << "PluginLib::NP_Initialize(" << web_plugin_info_.path.value()
115 << "): result=" << rv;
116 initialized_ = (rv == NPERR_NO_ERROR);
117 return rv;
120 void PluginLib::NP_Shutdown(void) {
121 DCHECK(initialized_);
122 entry_points_.np_shutdown();
125 NPError PluginLib::NP_ClearSiteData(const char* site,
126 uint64 flags,
127 uint64 max_age) {
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();
138 return NULL;
141 void PluginLib::PreventLibraryUnload() {
142 skip_unload_ = true;
145 PluginInstance* PluginLib::CreateInstance(const std::string& mime_type) {
146 PluginInstance* new_instance = new PluginInstance(this, mime_type);
147 instance_count_++;
148 DCHECK_NE(static_cast<PluginInstance*>(NULL), new_instance);
149 return new_instance;
152 void PluginLib::CloseInstance() {
153 instance_count_--;
154 // If a plugin is running in its own process it will get unloaded on process
155 // shutdown.
156 if ((instance_count_ == 0) && !defer_unload_)
157 Unload();
160 bool PluginLib::Load() {
161 if (library_)
162 return true;
164 bool rv = false;
165 base::NativeLibrary library = 0;
166 base::NativeLibraryLoadError error;
168 #if defined(OS_WIN)
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
174 // plugin.
175 if (web_plugin_info_.name.find(L"Windows Media Player") !=
176 std::wstring::npos) {
177 library = base::LoadNativeLibraryDynamically(web_plugin_info_.path);
178 } else {
179 library = base::LoadNativeLibrary(web_plugin_info_.path, &error);
181 #else
182 library = base::LoadNativeLibrary(web_plugin_info_.path, &error);
183 #endif
185 if (!library) {
186 LOG_IF(ERROR, PluginList::DebugPluginLoading())
187 << "Couldn't load plugin " << web_plugin_info_.path.value() << " "
188 << error.ToString();
189 return rv;
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);
197 #endif
199 rv = true; // assume success now
201 entry_points_.np_initialize =
202 (NP_InitializeFunc)base::GetFunctionPointerFromNativeLibrary(library,
203 "NP_Initialize");
204 if (entry_points_.np_initialize == 0)
205 rv = false;
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)
212 rv = false;
213 #endif
215 entry_points_.np_shutdown =
216 (NP_ShutdownFunc)base::GetFunctionPointerFromNativeLibrary(library,
217 "NP_Shutdown");
218 if (entry_points_.np_shutdown == 0)
219 rv = false;
221 if (rv) {
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)
226 rv = false;
227 #else
228 // On Linux and Mac, we get the plugin entry points during NP_Initialize.
229 #endif
232 if (rv) {
233 LOG_IF(ERROR, PluginList::DebugPluginLoading())
234 << "Plugin " << web_plugin_info_.path.value()
235 << " loaded successfully.";
236 library_ = library;
237 } else {
238 LOG_IF(ERROR, PluginList::DebugPluginLoading())
239 << "Plugin " << web_plugin_info_.path.value()
240 << " failed to load, unloading.";
241 base::UnloadNativeLibrary(library);
244 return rv;
247 // This is a helper to help perform a delayed NP_Shutdown and FreeLibrary on the
248 // plugin dll.
249 void FreePluginLibraryHelper(const base::FilePath& path,
250 base::NativeLibrary library,
251 NP_ShutdownFunc shutdown_func) {
252 if (shutdown_func) {
253 // Don't call NP_Shutdown if the library has been reloaded since this task
254 // was posted.
255 bool reloaded = false;
256 if (g_loaded_libs) {
257 for (size_t i = 0; i < g_loaded_libs->size(); ++i) {
258 if ((*g_loaded_libs)[i]->plugin_info().path == path) {
259 reloaded = true;
260 break;
264 if (!reloaded)
265 shutdown_func();
268 if (library) {
269 // Always call base::UnloadNativeLibrary so that the system reference
270 // count is decremented.
271 base::UnloadNativeLibrary(library);
275 void PluginLib::Unload() {
276 if (library_) {
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.
281 #if USE(JSC)
282 // The plugin NPAPI instances may still be around. Delay the
283 // NP_Shutdown and FreeLibrary calls at least till the next
284 // peek message.
285 defer_unload = true;
286 #endif
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(
293 FROM_HERE,
294 base::Bind(&FreePluginLibraryHelper,
295 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