OLD | NEW |
| (Empty) |
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 "chrome/common/chrome_plugin_lib.h" | |
6 | |
7 #include "base/command_line.h" | |
8 #include "base/hash_tables.h" | |
9 #include "base/message_loop.h" | |
10 #include "base/metrics/histogram.h" | |
11 #include "base/path_service.h" | |
12 #include "base/perftimer.h" | |
13 #include "base/string_util.h" | |
14 #include "base/threading/platform_thread.h" | |
15 #include "base/threading/thread.h" | |
16 #include "chrome/common/chrome_counters.h" | |
17 #include "chrome/common/chrome_paths.h" | |
18 #include "chrome/common/chrome_switches.h" | |
19 #include "content/common/notification_service.h" | |
20 #include "webkit/plugins/npapi/plugin_list.h" | |
21 | |
22 #if defined(OS_WIN) | |
23 #include "base/win/registry.h" | |
24 #endif | |
25 | |
26 using base::TimeDelta; | |
27 | |
28 // TODO(port): revisit when plugins happier | |
29 #if defined(OS_WIN) | |
30 const wchar_t ChromePluginLib::kRegistryChromePlugins[] = | |
31 L"Software\\Google\\Chrome\\Plugins"; | |
32 static const wchar_t kRegistryLoadOnStartup[] = L"LoadOnStartup"; | |
33 static const wchar_t kRegistryPath[] = L"Path"; | |
34 #endif | |
35 | |
36 typedef base::hash_map<FilePath, scoped_refptr<ChromePluginLib> > | |
37 PluginMap; | |
38 | |
39 // A map of all the instantiated plugins. | |
40 static PluginMap* g_loaded_libs; | |
41 | |
42 // The thread plugins are loaded and used in, lazily initialized upon | |
43 // the first creation call. | |
44 static base::PlatformThreadId g_plugin_thread_id = 0; | |
45 static MessageLoop* g_plugin_thread_loop = NULL; | |
46 | |
47 static bool IsSingleProcessMode() { | |
48 // We don't support ChromePlugins in single-process mode. | |
49 return CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess); | |
50 } | |
51 | |
52 // static | |
53 bool ChromePluginLib::IsInitialized() { | |
54 return (g_loaded_libs != NULL); | |
55 } | |
56 | |
57 // static | |
58 ChromePluginLib* ChromePluginLib::Create(const FilePath& filename, | |
59 const CPBrowserFuncs* bfuncs) { | |
60 // Keep a map of loaded plugins to ensure we only load each library once. | |
61 if (!g_loaded_libs) { | |
62 g_loaded_libs = new PluginMap(); | |
63 g_plugin_thread_id = base::PlatformThread::CurrentId(); | |
64 g_plugin_thread_loop = MessageLoop::current(); | |
65 } | |
66 DCHECK(IsPluginThread()); | |
67 | |
68 PluginMap::const_iterator iter = g_loaded_libs->find(filename); | |
69 if (iter != g_loaded_libs->end()) | |
70 return iter->second; | |
71 | |
72 scoped_refptr<ChromePluginLib> plugin(new ChromePluginLib(filename)); | |
73 if (!plugin->CP_Initialize(bfuncs)) | |
74 return NULL; | |
75 | |
76 (*g_loaded_libs)[filename] = plugin; | |
77 return plugin; | |
78 } | |
79 | |
80 // static | |
81 ChromePluginLib* ChromePluginLib::Find(const FilePath& filename) { | |
82 if (g_loaded_libs) { | |
83 PluginMap::const_iterator iter = g_loaded_libs->find(filename); | |
84 if (iter != g_loaded_libs->end()) | |
85 return iter->second; | |
86 } | |
87 return NULL; | |
88 } | |
89 | |
90 // static | |
91 void ChromePluginLib::Destroy(const FilePath& filename) { | |
92 DCHECK(g_loaded_libs); | |
93 PluginMap::iterator iter = g_loaded_libs->find(filename); | |
94 if (iter != g_loaded_libs->end()) { | |
95 iter->second->Unload(); | |
96 g_loaded_libs->erase(iter); | |
97 } | |
98 } | |
99 | |
100 // static | |
101 bool ChromePluginLib::IsPluginThread() { | |
102 return base::PlatformThread::CurrentId() == g_plugin_thread_id; | |
103 } | |
104 | |
105 // static | |
106 MessageLoop* ChromePluginLib::GetPluginThreadLoop() { | |
107 return g_plugin_thread_loop; | |
108 } | |
109 | |
110 // static | |
111 void ChromePluginLib::RegisterPluginsWithNPAPI() { | |
112 // We don't support ChromePlugins in single-process mode. | |
113 if (IsSingleProcessMode()) | |
114 return; | |
115 | |
116 FilePath path; | |
117 // Register Gears, if available. | |
118 if (PathService::Get(chrome::FILE_GEARS_PLUGIN, &path)) | |
119 webkit::npapi::PluginList::Singleton()->AddExtraPluginPath(path); | |
120 } | |
121 | |
122 static void LogPluginLoadTime(const TimeDelta &time) { | |
123 UMA_HISTOGRAM_TIMES("Gears.LoadTime", time); | |
124 } | |
125 | |
126 // static | |
127 void ChromePluginLib::LoadChromePlugins(const CPBrowserFuncs* bfuncs) { | |
128 static bool loaded = false; | |
129 if (loaded) | |
130 return; | |
131 loaded = true; | |
132 | |
133 // We don't support ChromePlugins in single-process mode. | |
134 if (IsSingleProcessMode()) | |
135 return; | |
136 | |
137 FilePath path; | |
138 if (!PathService::Get(chrome::FILE_GEARS_PLUGIN, &path)) | |
139 return; | |
140 | |
141 PerfTimer timer; | |
142 ChromePluginLib::Create(path, bfuncs); | |
143 LogPluginLoadTime(timer.Elapsed()); | |
144 | |
145 // TODO(mpcomplete): disabled loading of plugins from the registry until we | |
146 // phase out registry keys from the gears installer. | |
147 #if 0 | |
148 for (RegistryKeyIterator iter(HKEY_CURRENT_USER, kRegistryChromePlugins); | |
149 iter.Valid(); ++iter) { | |
150 // Use the registry to gather plugin across the file system. | |
151 std::wstring reg_path = kRegistryChromePlugins; | |
152 reg_path.append(L"\\"); | |
153 reg_path.append(iter.Name()); | |
154 base::win::RegKey key(HKEY_CURRENT_USER, reg_path.c_str()); | |
155 | |
156 DWORD is_persistent = 0; | |
157 key.ReadValueDW(kRegistryLoadOnStartup, &is_persistent); | |
158 if (is_persistent) { | |
159 std::wstring path; | |
160 if (key.ReadValue(kRegistryPath, &path) == ERROR_SUCCESS) { | |
161 ChromePluginLib::Create(path, bfuncs); | |
162 } | |
163 } | |
164 } | |
165 #endif | |
166 } | |
167 | |
168 // static | |
169 void ChromePluginLib::UnloadAllPlugins() { | |
170 if (g_loaded_libs) { | |
171 PluginMap::iterator it; | |
172 for (PluginMap::iterator it = g_loaded_libs->begin(); | |
173 it != g_loaded_libs->end(); ++it) { | |
174 it->second->Unload(); | |
175 } | |
176 delete g_loaded_libs; | |
177 g_loaded_libs = NULL; | |
178 } | |
179 } | |
180 | |
181 const CPPluginFuncs& ChromePluginLib::functions() const { | |
182 DCHECK(initialized_); | |
183 DCHECK(IsPluginThread()); | |
184 return plugin_funcs_; | |
185 } | |
186 | |
187 ChromePluginLib::ChromePluginLib(const FilePath& filename) | |
188 : filename_(filename), | |
189 #if defined(OS_WIN) | |
190 module_(0), | |
191 #endif | |
192 initialized_(false), | |
193 CP_VersionNegotiate_(NULL), | |
194 CP_Initialize_(NULL), | |
195 CP_Test_(NULL) { | |
196 memset((void*)&plugin_funcs_, 0, sizeof(plugin_funcs_)); | |
197 } | |
198 | |
199 ChromePluginLib::~ChromePluginLib() { | |
200 } | |
201 | |
202 bool ChromePluginLib::CP_Initialize(const CPBrowserFuncs* bfuncs) { | |
203 VLOG(1) << "ChromePluginLib::CP_Initialize(" << filename_.value() | |
204 << "): initialized=" << initialized_; | |
205 if (initialized_) | |
206 return true; | |
207 | |
208 if (!Load()) | |
209 return false; | |
210 | |
211 if (CP_VersionNegotiate_) { | |
212 uint16 selected_version = 0; | |
213 CPError rv = CP_VersionNegotiate_(CP_VERSION, CP_VERSION, | |
214 &selected_version); | |
215 if ((rv != CPERR_SUCCESS) || (selected_version != CP_VERSION)) | |
216 return false; | |
217 } | |
218 | |
219 plugin_funcs_.size = sizeof(plugin_funcs_); | |
220 CPError rv = CP_Initialize_(cpid(), bfuncs, &plugin_funcs_); | |
221 initialized_ = (rv == CPERR_SUCCESS) && | |
222 (CP_GET_MAJOR_VERSION(plugin_funcs_.version) == CP_MAJOR_VERSION) && | |
223 (CP_GET_MINOR_VERSION(plugin_funcs_.version) <= CP_MINOR_VERSION); | |
224 VLOG(1) << "ChromePluginLib::CP_Initialize(" << filename_.value() | |
225 << "): initialized=" << initialized_ << "): result=" << rv; | |
226 | |
227 return initialized_; | |
228 } | |
229 | |
230 void ChromePluginLib::CP_Shutdown() { | |
231 DCHECK(initialized_); | |
232 functions().shutdown(); | |
233 initialized_ = false; | |
234 memset((void*)&plugin_funcs_, 0, sizeof(plugin_funcs_)); | |
235 } | |
236 | |
237 int ChromePluginLib::CP_Test(void* param) { | |
238 DCHECK(initialized_); | |
239 if (!CP_Test_) | |
240 return -1; | |
241 return CP_Test_(param); | |
242 } | |
243 | |
244 bool ChromePluginLib::Load() { | |
245 #if !defined(OS_WIN) | |
246 // Mac and Linux won't implement Gears. | |
247 return false; | |
248 #else | |
249 DCHECK(module_ == 0); | |
250 | |
251 module_ = LoadLibrary(filename_.value().c_str()); | |
252 if (module_ == 0) | |
253 return false; | |
254 | |
255 // required initialization function | |
256 CP_Initialize_ = reinterpret_cast<CP_InitializeFunc> | |
257 (GetProcAddress(module_, "CP_Initialize")); | |
258 | |
259 if (!CP_Initialize_) { | |
260 FreeLibrary(module_); | |
261 module_ = 0; | |
262 return false; | |
263 } | |
264 | |
265 // optional version negotiation function | |
266 CP_VersionNegotiate_ = reinterpret_cast<CP_VersionNegotiateFunc> | |
267 (GetProcAddress(module_, "CP_VersionNegotiate")); | |
268 | |
269 // optional test function | |
270 CP_Test_ = reinterpret_cast<CP_TestFunc> | |
271 (GetProcAddress(module_, "CP_Test")); | |
272 | |
273 return true; | |
274 #endif | |
275 } | |
276 | |
277 void ChromePluginLib::Unload() { | |
278 NotificationService::current()->Notify( | |
279 NotificationType::CHROME_PLUGIN_UNLOADED, | |
280 Source<ChromePluginLib>(this), | |
281 NotificationService::NoDetails()); | |
282 | |
283 if (initialized_) | |
284 CP_Shutdown(); | |
285 | |
286 #if defined(OS_WIN) | |
287 if (module_) { | |
288 FreeLibrary(module_); | |
289 module_ = 0; | |
290 } | |
291 #endif | |
292 } | |
OLD | NEW |