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 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 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 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 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 81 PluginLib::~PluginLib() { 82 base::StatsCounter(kPluginLibrariesLoadedCounter).Decrement(); 83 if (saved_data_ != 0) { 84 // TODO - delete the savedData object here 85 } 86 } 87 88 NPPluginFuncs* PluginLib::functions() { 89 return &plugin_funcs_; 90 } 91 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 126 void PluginLib::NP_Shutdown(void) { 127 DCHECK(initialized_); 128 entry_points_.np_shutdown(); 129 } 130 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 140 char** PluginLib::NP_GetSitesWithData() { 141 DCHECK(initialized_); 142 if (plugin_funcs_.getsiteswithdata) 143 return plugin_funcs_.getsiteswithdata(); 144 return NULL; 145 } 146 147 void PluginLib::PreventLibraryUnload() { 148 skip_unload_ = true; 149 } 150 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 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 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. 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 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 330 void PluginLib::Shutdown() { 331 if (initialized_) { 332 NP_Shutdown(); 333 initialized_ = false; 334 } 335 } 336 337 } // namespace content 338