// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "config.h" #include "webkit/glue/plugins/plugin_lib.h" #include "base/file_util.h" #include "base/file_version_info.h" #include "base/logging.h" #include "base/message_loop.h" #include "base/path_service.h" #include "base/stats_counters.h" #include "base/string_util.h" #include "base/sys_string_conversions.h" #include "base/task.h" #include "webkit/activex_shim/npp_impl.h" #include "webkit/default_plugin/plugin_main.h" #include "webkit/glue/glue_util.h" #include "webkit/glue/webplugin.h" #include "webkit/glue/webkit_glue.h" #include "webkit/glue/plugins/plugin_instance.h" #include "webkit/glue/plugins/plugin_host.h" #include "webkit/glue/plugins/plugin_list.h" #include "net/base/mime_util.h" namespace NPAPI { const wchar_t kPluginLibrariesLoadedCounter[] = L"PluginLibrariesLoaded"; const wchar_t kPluginInstancesActiveCounter[] = L"PluginInstancesActive"; PluginLib::PluginMap* PluginLib::loaded_libs_; PluginLib* PluginLib::CreatePluginLib(const std::wstring& filename) { // We can only have one PluginLib object per plugin as it controls the per // instance function calls (i.e. NP_Initialize and NP_Shutdown). So we keep // a (non-ref counted) map of PluginLib objects. if (!loaded_libs_) loaded_libs_ = new PluginMap(); PluginMap::const_iterator iter = loaded_libs_->find(filename); if (iter != loaded_libs_->end()) return iter->second; static const InternalPluginInfo activex_shim_info = { {kActiveXShimFileName, L"ActiveX Plug-in", L"ActiveX Plug-in provides a shim to support ActiveX controls", L"1, 0, 0, 1", L"application/x-oleobject|application/oleobject|" L"application/x-ms-wmp|application/asx|video/x-ms-asf-plugin|" L"application/x-mplayer2|video/x-ms-asf|video/x-ms-wm|audio/x-ms-wma|" L"audio/x-ms-wax|video/x-ms-wmv|video/x-ms-wvx", L"*|*|*|*|*|*|asf,asx,*|wm,*|wma,*|wax,*|wmv,*|wvx,*", L"" }, activex_shim::ActiveX_Shim_NP_GetEntryPoints, activex_shim::ActiveX_Shim_NP_Initialize, activex_shim::ActiveX_Shim_NP_Shutdown }; static const InternalPluginInfo default_null_plugin_info = { {kDefaultPluginDllName, L"Default Plug-in", L"Provides functionality for installing third-party plug-ins", L"1, 0, 0, 1", L"*", L"", L"" }, default_plugin::NP_GetEntryPoints, default_plugin::NP_Initialize, default_plugin::NP_Shutdown }; WebPluginInfo* info = NULL; const InternalPluginInfo* internal_plugin_info = NULL; if (filename == activex_shim_info.version_info.file_name) { info = CreateWebPluginInfo(activex_shim_info.version_info); internal_plugin_info = &activex_shim_info; } else if (filename == default_null_plugin_info.version_info.file_name) { info = CreateWebPluginInfo(default_null_plugin_info.version_info); internal_plugin_info = &default_null_plugin_info; } else { info = ReadWebPluginInfo(filename); if (!info) { DLOG(INFO) << "This file isn't a valid NPAPI plugin: " << filename; return NULL; } } return new PluginLib(info, internal_plugin_info); } void PluginLib::UnloadAllPlugins() { if (loaded_libs_) { PluginMap::iterator lib_index; for (lib_index = loaded_libs_->begin(); lib_index != loaded_libs_->end(); ++lib_index) { lib_index->second->Unload(); } delete loaded_libs_; loaded_libs_ = NULL; } } void PluginLib::ShutdownAllPlugins() { if (loaded_libs_) { PluginMap::iterator lib_index; for (lib_index = loaded_libs_->begin(); lib_index != loaded_libs_->end(); ++lib_index) { lib_index->second->Shutdown(); } } } PluginLib::PluginLib(WebPluginInfo* info, const InternalPluginInfo* internal_plugin_info) : web_plugin_info_(info), module_(0), initialized_(false), saved_data_(0), instance_count_(0) { StatsCounter(kPluginLibrariesLoadedCounter).Increment(); memset((void*)&plugin_funcs_, 0, sizeof(plugin_funcs_)); (*loaded_libs_)[info->file] = this; if (internal_plugin_info) { internal_ = true; NP_Initialize_ = internal_plugin_info->np_initialize; NP_GetEntryPoints_ = internal_plugin_info->np_getentrypoints; NP_Shutdown_ = internal_plugin_info->np_shutdown; } else { internal_ = false; } } PluginLib::~PluginLib() { StatsCounter(kPluginLibrariesLoadedCounter).Decrement(); if (saved_data_ != 0) { // TODO - delete the savedData object here } } NPPluginFuncs *PluginLib::functions() { return &plugin_funcs_; } bool PluginLib::SupportsType(const std::string &mime_type, bool allow_wildcard) { // Webkit will ask for a plugin to handle empty mime types. if (mime_type.empty()) return false; for (size_t i = 0; i < web_plugin_info_->mime_types.size(); ++i) { const WebPluginMimeType& mime_info = web_plugin_info_->mime_types[i]; if (net::MatchesMimeType(mime_info.mime_type, mime_type)) { if (!allow_wildcard && (mime_info.mime_type == "*")) { continue; } return true; } } return false; } NPError PluginLib::NP_Initialize() { if (initialized_) return NPERR_NO_ERROR; if (!Load()) return NPERR_MODULE_LOAD_FAILED_ERROR; PluginHost *host = PluginHost::Singleton(); if (host == 0) return NPERR_GENERIC_ERROR; NPError rv = NP_Initialize_(host->host_functions()); initialized_ = (rv == NPERR_NO_ERROR); return rv; } void PluginLib::NP_Shutdown(void) { DCHECK(initialized_); NP_Shutdown_(); } PluginInstance *PluginLib::CreateInstance(const std::string &mime_type) { PluginInstance *new_instance = new PluginInstance(this, mime_type); instance_count_++; StatsCounter(kPluginInstancesActiveCounter).Increment(); DCHECK(new_instance != 0); return new_instance; } void PluginLib::CloseInstance() { StatsCounter(kPluginInstancesActiveCounter).Decrement(); instance_count_--; // If a plugin is running in its own process it will get unloaded on process // shutdown. if ((instance_count_ == 0) && webkit_glue::IsPluginRunningInRendererProcess()) { Unload(); loaded_libs_->erase(web_plugin_info_->file); if (loaded_libs_->empty()) { delete loaded_libs_; loaded_libs_ = NULL; } } } bool PluginLib::Load() { bool rv = false; HMODULE module = 0; if (!internal_) { if (module_ != 0) return rv; module = LoadPluginHelper(web_plugin_info_->file); if (module == 0) return rv; rv = true; // assume success now NP_Initialize_ = (NP_InitializeFunc)GetProcAddress( module, "NP_Initialize"); if (NP_Initialize_ == 0) rv = false; NP_GetEntryPoints_ = (NP_GetEntryPointsFunc)GetProcAddress( module, "NP_GetEntryPoints"); if (NP_GetEntryPoints_ == 0) rv = false; NP_Shutdown_ = (NP_ShutdownFunc)GetProcAddress( module, "NP_Shutdown"); if (NP_Shutdown_ == 0) rv = false; } else { rv = true; } if (rv) { plugin_funcs_.size = sizeof(plugin_funcs_); plugin_funcs_.version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR; if (NP_GetEntryPoints_(&plugin_funcs_) != NPERR_NO_ERROR) rv = false; } if (!internal_) { if (rv) module_ = module; else FreeLibrary(module); } return rv; } HMODULE PluginLib::LoadPluginHelper(const std::wstring plugin_file) { // Switch the current directory to the plugin directory as the plugin // may have dependencies on dlls in this directory. bool restore_directory = false; std::wstring current_directory; if (PathService::Get(base::DIR_CURRENT, ¤t_directory)) { std::wstring plugin_path = file_util::GetDirectoryFromPath( plugin_file); if (!plugin_path.empty()) { PathService::SetCurrentDirectory(plugin_path); restore_directory = true; } } HMODULE module = LoadLibrary(plugin_file.c_str()); if (restore_directory) PathService::SetCurrentDirectory(current_directory); return module; } // This class implements delayed NP_Shutdown and FreeLibrary on the plugin dll. class FreePluginLibraryTask : public Task { public: FreePluginLibraryTask(HMODULE module, NP_ShutdownFunc shutdown_func) : module_(module), NP_Shutdown_(shutdown_func) { } ~FreePluginLibraryTask() {} void Run() { if (NP_Shutdown_) NP_Shutdown_(); if (module_) { FreeLibrary(module_); module_ = NULL; } } private: HMODULE module_; NP_ShutdownFunc NP_Shutdown_; DISALLOW_EVIL_CONSTRUCTORS(FreePluginLibraryTask); }; void PluginLib::Unload() { if (!internal_ && module_) { // In case of single process mode, a plugin can delete itself // by executing a script. So delay the unloading of the DLL // so that the plugin will have a chance to unwind. bool defer_unload = webkit_glue::IsPluginRunningInRendererProcess(); #if USE(JSC) // The plugin NPAPI instances may still be around. Delay the // NP_Shutdown and FreeLibrary calls at least till the next // peek message. defer_unload = true; #endif if (defer_unload) { FreePluginLibraryTask* free_library_task = new FreePluginLibraryTask(module_, NP_Shutdown_); MessageLoop::current()->PostTask(FROM_HERE, free_library_task); } else { Shutdown(); FreeLibrary(module_); } module_ = 0; } } void PluginLib::Shutdown() { if (initialized_ && !internal_) { NP_Shutdown(); initialized_ = false; } } WebPluginInfo* PluginLib::CreateWebPluginInfo(const PluginVersionInfo& pvi) { std::vector mime_types, file_extensions; std::vector descriptions; SplitString(base::SysWideToNativeMB(pvi.mime_types), '|', &mime_types); SplitString(base::SysWideToNativeMB(pvi.file_extents), '|', &file_extensions); SplitString(pvi.file_open_names, '|', &descriptions); if (mime_types.empty()) return NULL; WebPluginInfo *info = new WebPluginInfo(); info->name = pvi.product_name; info->desc = pvi.file_description; info->version = pvi.file_version; info->file = StringToLowerASCII(pvi.file_name); for (size_t i = 0; i < mime_types.size(); ++i) { WebPluginMimeType mime_type; mime_type.mime_type = StringToLowerASCII(mime_types[i]); if (file_extensions.size() > i) SplitString(file_extensions[i], ',', &mime_type.file_extensions); if (descriptions.size() > i) { mime_type.description = descriptions[i]; // Remove the extension list from the description. size_t ext = mime_type.description.find(L"(*"); if (ext != std::wstring::npos) { if (ext > 1 && mime_type.description[ext -1] == ' ') ext--; mime_type.description.erase(ext); } } info->mime_types.push_back(mime_type); } return info; } WebPluginInfo* PluginLib::ReadWebPluginInfo(const std::wstring &filename) { // On windows, the way we get the mime types for the library is // to check the version information in the DLL itself. This // will be a string of the format: |||... // For example: // video/quicktime|audio/aiff|image/jpeg scoped_ptr version_info( FileVersionInfo::CreateFileVersionInfo(filename)); if (!version_info.get()) return NULL; PluginVersionInfo pvi; version_info->GetValue(L"MIMEType", &pvi.mime_types); version_info->GetValue(L"FileExtents", &pvi.file_extents); version_info->GetValue(L"FileOpenName", &pvi.file_open_names); pvi.product_name = version_info->product_name(); pvi.file_description = version_info->file_description(); pvi.file_version = version_info->file_version(); pvi.file_name = filename; return CreateWebPluginInfo(pvi); } } // namespace NPAPI