• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "content/child/npapi/plugin_lib.h"
6 
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"
15 
16 namespace content {
17 
18 const char kPluginLibrariesLoadedCounter[] = "PluginLibrariesLoaded";
19 const char kPluginInstancesActiveCounter[] = "PluginInstancesActive";
20 
21 // A list of all the instantiated plugins.
22 static std::vector<scoped_refptr<PluginLib> >* g_loaded_libs;
23 
CreatePluginLib(const base::FilePath & filename)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> >;
30 
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();
34   }
35 
36   WebPluginInfo info;
37   if (!PluginList::Singleton()->ReadPluginInfo(filename, &info))
38     return NULL;
39 
40   return new PluginLib(info);
41 }
42 
UnloadAllPlugins()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();
51 
52     if (g_loaded_libs && g_loaded_libs->empty()) {
53       delete g_loaded_libs;
54       g_loaded_libs = NULL;
55     }
56   }
57 }
58 
ShutdownAllPlugins()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();
63   }
64 }
65 
PluginLib(const WebPluginInfo & info)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));
77 
78   memset(&entry_points_, 0, sizeof(entry_points_));
79 }
80 
~PluginLib()81 PluginLib::~PluginLib() {
82   base::StatsCounter(kPluginLibrariesLoadedCounter).Decrement();
83   if (saved_data_ != 0) {
84     // TODO - delete the savedData object here
85   }
86 }
87 
functions()88 NPPluginFuncs* PluginLib::functions() {
89   return &plugin_funcs_;
90 }
91 
NP_Initialize()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;
98 
99   if (!Load())
100     return NPERR_MODULE_LOAD_FAILED_ERROR;
101 
102   PluginHost* host = PluginHost::Singleton();
103   if (host == 0)
104     return NPERR_GENERIC_ERROR;
105 
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_);
116   }
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;
124 }
125 
NP_Shutdown(void)126 void PluginLib::NP_Shutdown(void) {
127   DCHECK(initialized_);
128   entry_points_.np_shutdown();
129 }
130 
NP_ClearSiteData(const char * site,uint64 flags,uint64 max_age)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;
138 }
139 
NP_GetSitesWithData()140 char** PluginLib::NP_GetSitesWithData() {
141   DCHECK(initialized_);
142   if (plugin_funcs_.getsiteswithdata)
143     return plugin_funcs_.getsiteswithdata();
144   return NULL;
145 }
146 
PreventLibraryUnload()147 void PluginLib::PreventLibraryUnload() {
148   skip_unload_ = true;
149 }
150 
CreateInstance(const std::string & mime_type)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;
157 }
158 
CloseInstance()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();
166 }
167 
Load()168 bool PluginLib::Load() {
169   if (library_)
170     return true;
171 
172   bool rv = false;
173   base::NativeLibrary library = 0;
174   base::NativeLibraryLoadError error;
175 
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);
188   }
189 #else
190   library = base::LoadNativeLibrary(web_plugin_info_.path, &error);
191 #endif
192 
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;
198   }
199 
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
206 
207   rv = true;  // assume success now
208 
209   entry_points_.np_initialize =
210       (NP_InitializeFunc)base::GetFunctionPointerFromNativeLibrary(library,
211           "NP_Initialize");
212   if (entry_points_.np_initialize == 0)
213     rv = false;
214 
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
222 
223   entry_points_.np_shutdown =
224       (NP_ShutdownFunc)base::GetFunctionPointerFromNativeLibrary(library,
225           "NP_Shutdown");
226   if (entry_points_.np_shutdown == 0)
227     rv = false;
228 
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
238   }
239 
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);
250   }
251 
252   return rv;
253 }
254 
255 // This is a helper to help perform a delayed NP_Shutdown and FreeLibrary on the
256 // plugin dll.
FreePluginLibraryHelper(const base::FilePath & path,base::NativeLibrary library,NP_ShutdownFunc shutdown_func)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;
269         }
270       }
271     }
272     if (!reloaded)
273       shutdown_func();
274   }
275 
276   if (library) {
277     // Always call base::UnloadNativeLibrary so that the system reference
278     // count is decremented.
279     base::UnloadNativeLibrary(library);
280   }
281 }
282 
Unload()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
295 */
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_);
312       }
313     }
314 
315     library_ = NULL;
316   }
317 
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;
322     }
323   }
324   if (g_loaded_libs->empty()) {
325     delete g_loaded_libs;
326     g_loaded_libs = NULL;
327   }
328 }
329 
Shutdown()330 void PluginLib::Shutdown() {
331   if (initialized_) {
332     NP_Shutdown();
333     initialized_ = false;
334   }
335 }
336 
337 }  // namespace content
338