| // 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 "chrome/common/chrome_plugin_lib.h" |
| |
| #include "base/command_line.h" |
| #include "base/hash_tables.h" |
| #include "base/histogram.h" |
| #include "base/message_loop.h" |
| #include "base/path_service.h" |
| #include "base/perftimer.h" |
| #include "base/thread.h" |
| #if defined(OS_WIN) |
| #include "base/registry.h" |
| #endif |
| #include "base/string_util.h" |
| #include "chrome/common/chrome_counters.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/common/notification_service.h" |
| #include "chrome/common/chrome_paths.h" |
| #include "webkit/glue/plugins/plugin_list.h" |
| |
| using base::TimeDelta; |
| |
| // TODO(port): revisit when plugins happier |
| #if defined(OS_WIN) |
| const wchar_t ChromePluginLib::kRegistryChromePlugins[] = |
| L"Software\\Google\\Chrome\\Plugins"; |
| static const wchar_t kRegistryLoadOnStartup[] = L"LoadOnStartup"; |
| static const wchar_t kRegistryPath[] = L"Path"; |
| #endif |
| |
| typedef base::hash_map<FilePath, scoped_refptr<ChromePluginLib> > |
| PluginMap; |
| |
| // A map of all the instantiated plugins. |
| static PluginMap* g_loaded_libs; |
| |
| // The thread plugins are loaded and used in, lazily initialized upon |
| // the first creation call. |
| static PlatformThreadId g_plugin_thread_id = 0; |
| static MessageLoop* g_plugin_thread_loop = NULL; |
| |
| static bool IsSingleProcessMode() { |
| // We don't support ChromePlugins in single-process mode. |
| return CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess); |
| } |
| |
| // static |
| bool ChromePluginLib::IsInitialized() { |
| return (g_loaded_libs != NULL); |
| } |
| |
| // static |
| ChromePluginLib* ChromePluginLib::Create(const FilePath& filename, |
| const CPBrowserFuncs* bfuncs) { |
| // Keep a map of loaded plugins to ensure we only load each library once. |
| if (!g_loaded_libs) { |
| g_loaded_libs = new PluginMap(); |
| g_plugin_thread_id = PlatformThread::CurrentId(); |
| g_plugin_thread_loop = MessageLoop::current(); |
| } |
| DCHECK(IsPluginThread()); |
| |
| PluginMap::const_iterator iter = g_loaded_libs->find(filename); |
| if (iter != g_loaded_libs->end()) |
| return iter->second; |
| |
| scoped_refptr<ChromePluginLib> plugin(new ChromePluginLib(filename)); |
| if (!plugin->CP_Initialize(bfuncs)) |
| return NULL; |
| |
| (*g_loaded_libs)[filename] = plugin; |
| return plugin; |
| } |
| |
| // static |
| ChromePluginLib* ChromePluginLib::Find(const FilePath& filename) { |
| if (g_loaded_libs) { |
| PluginMap::const_iterator iter = g_loaded_libs->find(filename); |
| if (iter != g_loaded_libs->end()) |
| return iter->second; |
| } |
| return NULL; |
| } |
| |
| // static |
| void ChromePluginLib::Destroy(const FilePath& filename) { |
| DCHECK(g_loaded_libs); |
| PluginMap::iterator iter = g_loaded_libs->find(filename); |
| if (iter != g_loaded_libs->end()) { |
| iter->second->Unload(); |
| g_loaded_libs->erase(iter); |
| } |
| } |
| |
| // static |
| bool ChromePluginLib::IsPluginThread() { |
| return PlatformThread::CurrentId() == g_plugin_thread_id; |
| } |
| |
| // static |
| MessageLoop* ChromePluginLib::GetPluginThreadLoop() { |
| return g_plugin_thread_loop; |
| } |
| |
| // static |
| void ChromePluginLib::RegisterPluginsWithNPAPI() { |
| // We don't support ChromePlugins in single-process mode. |
| if (IsSingleProcessMode()) |
| return; |
| |
| FilePath path; |
| // Register Gears, if available. |
| if (PathService::Get(chrome::FILE_GEARS_PLUGIN, &path)) |
| NPAPI::PluginList::Singleton()->AddExtraPluginPath(path); |
| } |
| |
| static void LogPluginLoadTime(const TimeDelta &time) { |
| UMA_HISTOGRAM_TIMES("Gears.LoadTime", time); |
| } |
| |
| // static |
| void ChromePluginLib::LoadChromePlugins(const CPBrowserFuncs* bfuncs) { |
| static bool loaded = false; |
| if (loaded) |
| return; |
| loaded = true; |
| |
| // We don't support ChromePlugins in single-process mode. |
| if (IsSingleProcessMode()) |
| return; |
| |
| FilePath path; |
| if (!PathService::Get(chrome::FILE_GEARS_PLUGIN, &path)) |
| return; |
| |
| PerfTimer timer; |
| ChromePluginLib::Create(path, bfuncs); |
| LogPluginLoadTime(timer.Elapsed()); |
| |
| // TODO(mpcomplete): disabled loading of plugins from the registry until we |
| // phase out registry keys from the gears installer. |
| #if 0 |
| for (RegistryKeyIterator iter(HKEY_CURRENT_USER, kRegistryChromePlugins); |
| iter.Valid(); ++iter) { |
| // Use the registry to gather plugin across the file system. |
| std::wstring reg_path = kRegistryChromePlugins; |
| reg_path.append(L"\\"); |
| reg_path.append(iter.Name()); |
| RegKey key(HKEY_CURRENT_USER, reg_path.c_str()); |
| |
| DWORD is_persistent; |
| if (key.ReadValueDW(kRegistryLoadOnStartup, &is_persistent) && |
| is_persistent) { |
| std::wstring path; |
| if (key.ReadValue(kRegistryPath, &path)) { |
| ChromePluginLib::Create(path, bfuncs); |
| } |
| } |
| } |
| #endif |
| } |
| |
| // static |
| void ChromePluginLib::UnloadAllPlugins() { |
| if (g_loaded_libs) { |
| PluginMap::iterator it; |
| for (PluginMap::iterator it = g_loaded_libs->begin(); |
| it != g_loaded_libs->end(); ++it) { |
| it->second->Unload(); |
| } |
| delete g_loaded_libs; |
| g_loaded_libs = NULL; |
| } |
| } |
| |
| const CPPluginFuncs& ChromePluginLib::functions() const { |
| DCHECK(initialized_); |
| DCHECK(IsPluginThread()); |
| return plugin_funcs_; |
| } |
| |
| ChromePluginLib::ChromePluginLib(const FilePath& filename) |
| : filename_(filename), |
| #if defined(OS_WIN) |
| module_(0), |
| #endif |
| initialized_(false), |
| CP_VersionNegotiate_(NULL), |
| CP_Initialize_(NULL) { |
| memset((void*)&plugin_funcs_, 0, sizeof(plugin_funcs_)); |
| } |
| |
| ChromePluginLib::~ChromePluginLib() { |
| } |
| |
| bool ChromePluginLib::CP_Initialize(const CPBrowserFuncs* bfuncs) { |
| LOG(INFO) << "ChromePluginLib::CP_Initialize(" << filename_.value() << |
| "): initialized=" << initialized_; |
| if (initialized_) |
| return true; |
| |
| if (!Load()) |
| return false; |
| |
| if (CP_VersionNegotiate_) { |
| uint16 selected_version = 0; |
| CPError rv = CP_VersionNegotiate_(CP_VERSION, CP_VERSION, |
| &selected_version); |
| if ( (rv != CPERR_SUCCESS) || (selected_version != CP_VERSION)) |
| return false; |
| } |
| |
| plugin_funcs_.size = sizeof(plugin_funcs_); |
| CPError rv = CP_Initialize_(cpid(), bfuncs, &plugin_funcs_); |
| initialized_ = (rv == CPERR_SUCCESS) && |
| (CP_GET_MAJOR_VERSION(plugin_funcs_.version) == CP_MAJOR_VERSION) && |
| (CP_GET_MINOR_VERSION(plugin_funcs_.version) <= CP_MINOR_VERSION); |
| LOG(INFO) << "ChromePluginLib::CP_Initialize(" << filename_.value() << |
| "): initialized=" << initialized_ << |
| "): result=" << rv; |
| |
| return initialized_; |
| } |
| |
| void ChromePluginLib::CP_Shutdown() { |
| DCHECK(initialized_); |
| functions().shutdown(); |
| initialized_ = false; |
| memset((void*)&plugin_funcs_, 0, sizeof(plugin_funcs_)); |
| } |
| |
| int ChromePluginLib::CP_Test(void* param) { |
| DCHECK(initialized_); |
| if (!CP_Test_) |
| return -1; |
| return CP_Test_(param); |
| } |
| |
| bool ChromePluginLib::Load() { |
| #if !defined(OS_WIN) |
| // Mac and Linux won't implement Gears. |
| return false; |
| #else |
| DCHECK(module_ == 0); |
| |
| module_ = LoadLibrary(filename_.value().c_str()); |
| if (module_ == 0) |
| return false; |
| |
| // required initialization function |
| CP_Initialize_ = reinterpret_cast<CP_InitializeFunc> |
| (GetProcAddress(module_, "CP_Initialize")); |
| |
| if (!CP_Initialize_) { |
| FreeLibrary(module_); |
| module_ = 0; |
| return false; |
| } |
| |
| // optional version negotiation function |
| CP_VersionNegotiate_ = reinterpret_cast<CP_VersionNegotiateFunc> |
| (GetProcAddress(module_, "CP_VersionNegotiate")); |
| |
| // optional test function |
| CP_Test_ = reinterpret_cast<CP_TestFunc> |
| (GetProcAddress(module_, "CP_Test")); |
| |
| return true; |
| #endif |
| } |
| |
| void ChromePluginLib::Unload() { |
| NotificationService::current()->Notify( |
| NotificationType::CHROME_PLUGIN_UNLOADED, |
| Source<ChromePluginLib>(this), |
| NotificationService::NoDetails()); |
| |
| if (initialized_) |
| CP_Shutdown(); |
| |
| #if defined(OS_WIN) |
| if (module_) { |
| FreeLibrary(module_); |
| module_ = 0; |
| } |
| #endif |
| } |