| // 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<std::string> mime_types, file_extensions; |
| std::vector<std::wstring> 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: <type1>|<type2>|<type3>|... |
| // For example: |
| // video/quicktime|audio/aiff|image/jpeg |
| scoped_ptr<FileVersionInfo> 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 |
| |