[content shell] implement testRunner.overridePreference
[chromium-blink-merge.git] / content / browser / plugin_service_impl.cc
blobfa21c7d61491ed634706df8ba881793ef362d858
1 // Copyright (c) 2012 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.
5 #include "content/browser/plugin_service_impl.h"
7 #include "base/bind.h"
8 #include "base/command_line.h"
9 #include "base/compiler_specific.h"
10 #include "base/file_path.h"
11 #include "base/message_loop.h"
12 #include "base/message_loop_proxy.h"
13 #include "base/path_service.h"
14 #include "base/string_util.h"
15 #include "base/synchronization/waitable_event.h"
16 #include "base/threading/thread.h"
17 #include "base/utf_string_conversions.h"
18 #include "base/values.h"
19 #include "content/browser/ppapi_plugin_process_host.h"
20 #include "content/browser/renderer_host/render_process_host_impl.h"
21 #include "content/browser/renderer_host/render_view_host_impl.h"
22 #include "content/common/pepper_plugin_registry.h"
23 #include "content/common/plugin_messages.h"
24 #include "content/common/utility_messages.h"
25 #include "content/common/view_messages.h"
26 #include "content/public/browser/browser_thread.h"
27 #include "content/public/browser/content_browser_client.h"
28 #include "content/public/browser/plugin_service_filter.h"
29 #include "content/public/browser/resource_context.h"
30 #include "content/public/common/content_switches.h"
31 #include "content/public/common/process_type.h"
32 #include "webkit/plugins/npapi/plugin_list.h"
33 #include "webkit/plugins/webplugininfo.h"
35 #if defined(OS_WIN)
36 #include "webkit/plugins/npapi/plugin_constants_win.h"
37 #endif
39 #if defined(OS_POSIX)
40 #include "content/browser/plugin_loader_posix.h"
41 #endif
43 #if defined(OS_POSIX) && !defined(OS_OPENBSD) && !defined(OS_ANDROID)
44 using ::base::files::FilePathWatcher;
45 #endif
47 namespace content {
48 namespace {
50 // Callback set on the PluginList to assert that plugin loading happens on the
51 // correct thread.
52 #if defined(OS_WIN)
53 void WillLoadPluginsCallbackWin(
54 base::SequencedWorkerPool::SequenceToken token) {
55 CHECK(BrowserThread::GetBlockingPool()->IsRunningSequenceOnCurrentThread(
56 token));
58 #else
59 void WillLoadPluginsCallbackPosix() {
60 CHECK(false) << "Plugin loading should happen out-of-process.";
62 #endif
64 } // namespace
66 #if defined(OS_MACOSX)
67 static void NotifyPluginsOfActivation() {
68 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
70 for (PluginProcessHostIterator iter; !iter.Done(); ++iter)
71 iter->OnAppActivation();
73 #endif
74 #if defined(OS_POSIX) && !defined(OS_OPENBSD) && !defined(OS_ANDROID)
75 // Delegate class for monitoring directories.
76 class PluginDirWatcherDelegate : public FilePathWatcher::Delegate {
77 virtual void OnFilePathChanged(const FilePath& path) OVERRIDE {
78 VLOG(1) << "Watched path changed: " << path.value();
79 // Make the plugin list update itself
80 webkit::npapi::PluginList::Singleton()->RefreshPlugins();
81 BrowserThread::PostTask(
82 BrowserThread::UI, FROM_HERE,
83 base::Bind(&PluginService::PurgePluginListCache,
84 static_cast<BrowserContext*>(NULL), false));
87 virtual void OnFilePathError(const FilePath& path) OVERRIDE {
88 // TODO(pastarmovj): Add some sensible error handling. Maybe silently
89 // stopping the watcher would be enough. Or possibly restart it.
90 NOTREACHED();
93 protected:
94 virtual ~PluginDirWatcherDelegate() {}
96 #endif
98 // static
99 PluginService* PluginService::GetInstance() {
100 return PluginServiceImpl::GetInstance();
103 void PluginService::PurgePluginListCache(BrowserContext* browser_context,
104 bool reload_pages) {
105 for (RenderProcessHost::iterator it = RenderProcessHost::AllHostsIterator();
106 !it.IsAtEnd(); it.Advance()) {
107 RenderProcessHost* host = it.GetCurrentValue();
108 if (!browser_context || host->GetBrowserContext() == browser_context)
109 host->Send(new ViewMsg_PurgePluginListCache(reload_pages));
113 // static
114 PluginServiceImpl* PluginServiceImpl::GetInstance() {
115 return Singleton<PluginServiceImpl>::get();
118 PluginServiceImpl::PluginServiceImpl()
119 : plugin_list_(NULL), filter_(NULL) {
122 PluginServiceImpl::~PluginServiceImpl() {
123 #if defined(OS_WIN)
124 // Release the events since they're owned by RegKey, not WaitableEvent.
125 hkcu_watcher_.StopWatching();
126 hklm_watcher_.StopWatching();
127 if (hkcu_event_.get())
128 hkcu_event_->Release();
129 if (hklm_event_.get())
130 hklm_event_->Release();
131 #endif
132 // Make sure no plugin channel requests have been leaked.
133 DCHECK(pending_plugin_clients_.empty());
136 void PluginServiceImpl::Init() {
137 if (!plugin_list_)
138 plugin_list_ = webkit::npapi::PluginList::Singleton();
140 #if defined(OS_WIN)
141 plugin_list_token_ = BrowserThread::GetBlockingPool()->GetSequenceToken();
142 plugin_list_->set_will_load_plugins_callback(
143 base::Bind(&WillLoadPluginsCallbackWin, plugin_list_token_));
144 #else
145 plugin_list_->set_will_load_plugins_callback(
146 base::Bind(&WillLoadPluginsCallbackPosix));
147 #endif
149 RegisterPepperPlugins();
151 GetContentClient()->AddNPAPIPlugins(plugin_list_);
153 // Load any specified on the command line as well.
154 const CommandLine* command_line = CommandLine::ForCurrentProcess();
155 FilePath path = command_line->GetSwitchValuePath(switches::kLoadPlugin);
156 if (!path.empty())
157 AddExtraPluginPath(path);
158 path = command_line->GetSwitchValuePath(switches::kExtraPluginDir);
159 if (!path.empty())
160 plugin_list_->AddExtraPluginDir(path);
163 void PluginServiceImpl::StartWatchingPlugins() {
164 // Start watching for changes in the plugin list. This means watching
165 // for changes in the Windows registry keys and on both Windows and POSIX
166 // watch for changes in the paths that are expected to contain plugins.
167 #if defined(OS_WIN)
168 if (hkcu_key_.Create(HKEY_CURRENT_USER,
169 webkit::npapi::kRegistryMozillaPlugins,
170 KEY_NOTIFY) == ERROR_SUCCESS) {
171 if (hkcu_key_.StartWatching() == ERROR_SUCCESS) {
172 hkcu_event_.reset(new base::WaitableEvent(hkcu_key_.watch_event()));
173 hkcu_watcher_.StartWatching(hkcu_event_.get(), this);
176 if (hklm_key_.Create(HKEY_LOCAL_MACHINE,
177 webkit::npapi::kRegistryMozillaPlugins,
178 KEY_NOTIFY) == ERROR_SUCCESS) {
179 if (hklm_key_.StartWatching() == ERROR_SUCCESS) {
180 hklm_event_.reset(new base::WaitableEvent(hklm_key_.watch_event()));
181 hklm_watcher_.StartWatching(hklm_event_.get(), this);
184 #endif
185 #if defined(OS_POSIX) && !defined(OS_OPENBSD) && !defined(OS_ANDROID)
186 // On ChromeOS the user can't install plugins anyway and on Windows all
187 // important plugins register themselves in the registry so no need to do that.
188 file_watcher_delegate_ = new PluginDirWatcherDelegate();
189 // Get the list of all paths for registering the FilePathWatchers
190 // that will track and if needed reload the list of plugins on runtime.
191 std::vector<FilePath> plugin_dirs;
192 plugin_list_->GetPluginDirectories(&plugin_dirs);
194 for (size_t i = 0; i < plugin_dirs.size(); ++i) {
195 // FilePathWatcher can not handle non-absolute paths under windows.
196 // We don't watch for file changes in windows now but if this should ever
197 // be extended to Windows these lines might save some time of debugging.
198 #if defined(OS_WIN)
199 if (!plugin_dirs[i].IsAbsolute())
200 continue;
201 #endif
202 FilePathWatcher* watcher = new FilePathWatcher();
203 VLOG(1) << "Watching for changes in: " << plugin_dirs[i].value();
204 BrowserThread::PostTask(
205 BrowserThread::FILE, FROM_HERE,
206 base::Bind(&PluginServiceImpl::RegisterFilePathWatcher, watcher,
207 plugin_dirs[i], file_watcher_delegate_));
208 file_watchers_.push_back(watcher);
210 #endif
213 PluginProcessHost* PluginServiceImpl::FindNpapiPluginProcess(
214 const FilePath& plugin_path) {
215 for (PluginProcessHostIterator iter; !iter.Done(); ++iter) {
216 if (iter->info().path == plugin_path)
217 return *iter;
220 return NULL;
223 PpapiPluginProcessHost* PluginServiceImpl::FindPpapiPluginProcess(
224 const FilePath& plugin_path,
225 const FilePath& profile_data_directory) {
226 for (PpapiPluginProcessHostIterator iter; !iter.Done(); ++iter) {
227 if (iter->plugin_path() == plugin_path &&
228 iter->profile_data_directory() == profile_data_directory) {
229 return *iter;
232 return NULL;
235 PpapiPluginProcessHost* PluginServiceImpl::FindPpapiBrokerProcess(
236 const FilePath& broker_path) {
237 for (PpapiBrokerProcessHostIterator iter; !iter.Done(); ++iter) {
238 if (iter->plugin_path() == broker_path)
239 return *iter;
242 return NULL;
245 PluginProcessHost* PluginServiceImpl::FindOrStartNpapiPluginProcess(
246 const FilePath& plugin_path) {
247 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
249 PluginProcessHost* plugin_host = FindNpapiPluginProcess(plugin_path);
250 if (plugin_host)
251 return plugin_host;
253 webkit::WebPluginInfo info;
254 if (!GetPluginInfoByPath(plugin_path, &info)) {
255 return NULL;
258 // This plugin isn't loaded by any plugin process, so create a new process.
259 scoped_ptr<PluginProcessHost> new_host(new PluginProcessHost());
260 if (!new_host->Init(info)) {
261 NOTREACHED(); // Init is not expected to fail.
262 return NULL;
264 return new_host.release();
267 PpapiPluginProcessHost* PluginServiceImpl::FindOrStartPpapiPluginProcess(
268 const FilePath& plugin_path,
269 const FilePath& profile_data_directory,
270 PpapiPluginProcessHost::PluginClient* client) {
271 #if defined(ENABLE_PLUGINS)
272 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
274 PpapiPluginProcessHost* plugin_host =
275 FindPpapiPluginProcess(plugin_path, profile_data_directory);
276 if (plugin_host)
277 return plugin_host;
279 // Validate that the plugin is actually registered.
280 PepperPluginInfo* info = GetRegisteredPpapiPluginInfo(plugin_path);
281 if (!info)
282 return NULL;
284 // This plugin isn't loaded by any plugin process, so create a new process.
285 return PpapiPluginProcessHost::CreatePluginHost(
286 *info, profile_data_directory,
287 client->GetResourceContext()->GetHostResolver());
288 #else
289 return NULL;
290 #endif
293 PpapiPluginProcessHost* PluginServiceImpl::FindOrStartPpapiBrokerProcess(
294 const FilePath& plugin_path) {
295 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
297 PpapiPluginProcessHost* plugin_host = FindPpapiBrokerProcess(plugin_path);
298 if (plugin_host)
299 return plugin_host;
301 // Validate that the plugin is actually registered.
302 PepperPluginInfo* info = GetRegisteredPpapiPluginInfo(plugin_path);
303 if (!info)
304 return NULL;
306 // TODO(ddorwin): Uncomment once out of process is supported.
307 // DCHECK(info->is_out_of_process);
309 // This broker isn't loaded by any broker process, so create a new process.
310 return PpapiPluginProcessHost::CreateBrokerHost(*info);
313 void PluginServiceImpl::OpenChannelToNpapiPlugin(
314 int render_process_id,
315 int render_view_id,
316 const GURL& url,
317 const GURL& page_url,
318 const std::string& mime_type,
319 PluginProcessHost::Client* client) {
320 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
321 DCHECK(!ContainsKey(pending_plugin_clients_, client));
322 pending_plugin_clients_.insert(client);
324 // Make sure plugins are loaded if necessary.
325 PluginServiceFilterParams params = {
326 render_process_id,
327 render_view_id,
328 page_url,
329 client->GetResourceContext()
331 GetPlugins(base::Bind(
332 &PluginServiceImpl::ForwardGetAllowedPluginForOpenChannelToPlugin,
333 base::Unretained(this), params, url, mime_type, client));
336 void PluginServiceImpl::OpenChannelToPpapiPlugin(
337 const FilePath& plugin_path,
338 const FilePath& profile_data_directory,
339 PpapiPluginProcessHost::PluginClient* client) {
340 PpapiPluginProcessHost* plugin_host = FindOrStartPpapiPluginProcess(
341 plugin_path, profile_data_directory, client);
342 if (plugin_host) {
343 plugin_host->OpenChannelToPlugin(client);
344 } else {
345 // Send error.
346 client->OnPpapiChannelOpened(IPC::ChannelHandle(), 0);
350 void PluginServiceImpl::OpenChannelToPpapiBroker(
351 const FilePath& path,
352 PpapiPluginProcessHost::BrokerClient* client) {
353 #if defined(ENABLE_PLUGINS)
354 PpapiPluginProcessHost* plugin_host = FindOrStartPpapiBrokerProcess(path);
355 if (plugin_host) {
356 plugin_host->OpenChannelToPlugin(client);
357 } else {
358 // Send error.
359 client->OnPpapiChannelOpened(IPC::ChannelHandle(), 0);
361 #endif
364 void PluginServiceImpl::CancelOpenChannelToNpapiPlugin(
365 PluginProcessHost::Client* client) {
366 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
367 DCHECK(ContainsKey(pending_plugin_clients_, client));
368 pending_plugin_clients_.erase(client);
371 void PluginServiceImpl::ForwardGetAllowedPluginForOpenChannelToPlugin(
372 const PluginServiceFilterParams& params,
373 const GURL& url,
374 const std::string& mime_type,
375 PluginProcessHost::Client* client,
376 const std::vector<webkit::WebPluginInfo>&) {
377 GetAllowedPluginForOpenChannelToPlugin(params.render_process_id,
378 params.render_view_id, url, params.page_url, mime_type, client,
379 params.resource_context);
382 void PluginServiceImpl::GetAllowedPluginForOpenChannelToPlugin(
383 int render_process_id,
384 int render_view_id,
385 const GURL& url,
386 const GURL& page_url,
387 const std::string& mime_type,
388 PluginProcessHost::Client* client,
389 ResourceContext* resource_context) {
390 webkit::WebPluginInfo info;
391 bool allow_wildcard = true;
392 bool found = GetPluginInfo(
393 render_process_id, render_view_id, resource_context,
394 url, page_url, mime_type, allow_wildcard,
395 NULL, &info, NULL);
396 FilePath plugin_path;
397 if (found)
398 plugin_path = info.path;
400 // Now we jump back to the IO thread to finish opening the channel.
401 BrowserThread::PostTask(
402 BrowserThread::IO, FROM_HERE,
403 base::Bind(&PluginServiceImpl::FinishOpenChannelToPlugin,
404 base::Unretained(this), plugin_path, client));
407 void PluginServiceImpl::FinishOpenChannelToPlugin(
408 const FilePath& plugin_path,
409 PluginProcessHost::Client* client) {
410 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
412 // Make sure it hasn't been canceled yet.
413 if (!ContainsKey(pending_plugin_clients_, client))
414 return;
415 pending_plugin_clients_.erase(client);
417 PluginProcessHost* plugin_host = FindOrStartNpapiPluginProcess(plugin_path);
418 if (plugin_host) {
419 client->OnFoundPluginProcessHost(plugin_host);
420 plugin_host->OpenChannelToPlugin(client);
421 } else {
422 client->OnError();
426 bool PluginServiceImpl::GetPluginInfoArray(
427 const GURL& url,
428 const std::string& mime_type,
429 bool allow_wildcard,
430 std::vector<webkit::WebPluginInfo>* plugins,
431 std::vector<std::string>* actual_mime_types) {
432 bool use_stale = false;
433 plugin_list_->GetPluginInfoArray(url, mime_type, allow_wildcard,
434 &use_stale, plugins, actual_mime_types);
435 return use_stale;
438 bool PluginServiceImpl::GetPluginInfo(int render_process_id,
439 int render_view_id,
440 ResourceContext* context,
441 const GURL& url,
442 const GURL& page_url,
443 const std::string& mime_type,
444 bool allow_wildcard,
445 bool* is_stale,
446 webkit::WebPluginInfo* info,
447 std::string* actual_mime_type) {
448 std::vector<webkit::WebPluginInfo> plugins;
449 std::vector<std::string> mime_types;
450 bool stale = GetPluginInfoArray(
451 url, mime_type, allow_wildcard, &plugins, &mime_types);
452 if (is_stale)
453 *is_stale = stale;
455 for (size_t i = 0; i < plugins.size(); ++i) {
456 if (!filter_ || filter_->ShouldUsePlugin(render_process_id,
457 render_view_id,
458 context,
459 url,
460 page_url,
461 &plugins[i])) {
462 *info = plugins[i];
463 if (actual_mime_type)
464 *actual_mime_type = mime_types[i];
465 return true;
468 return false;
471 bool PluginServiceImpl::GetPluginInfoByPath(const FilePath& plugin_path,
472 webkit::WebPluginInfo* info) {
473 std::vector<webkit::WebPluginInfo> plugins;
474 plugin_list_->GetPluginsNoRefresh(&plugins);
476 for (std::vector<webkit::WebPluginInfo>::iterator it = plugins.begin();
477 it != plugins.end();
478 ++it) {
479 if (it->path == plugin_path) {
480 *info = *it;
481 return true;
485 return false;
488 string16 PluginServiceImpl::GetPluginDisplayNameByPath(const FilePath& path) {
489 string16 plugin_name = path.LossyDisplayName();
490 webkit::WebPluginInfo info;
491 if (PluginService::GetInstance()->GetPluginInfoByPath(path, &info) &&
492 !info.name.empty()) {
493 plugin_name = info.name;
494 #if defined(OS_MACOSX)
495 // Many plugins on the Mac have .plugin in the actual name, which looks
496 // terrible, so look for that and strip it off if present.
497 const std::string kPluginExtension = ".plugin";
498 if (EndsWith(plugin_name, ASCIIToUTF16(kPluginExtension), true))
499 plugin_name.erase(plugin_name.length() - kPluginExtension.length());
500 #endif // OS_MACOSX
502 return plugin_name;
505 void PluginServiceImpl::GetPlugins(const GetPluginsCallback& callback) {
506 scoped_refptr<base::MessageLoopProxy> target_loop(
507 MessageLoop::current()->message_loop_proxy());
509 #if defined(OS_WIN)
510 BrowserThread::GetBlockingPool()->PostSequencedWorkerTaskWithShutdownBehavior(
511 plugin_list_token_,
512 FROM_HERE,
513 base::Bind(&PluginServiceImpl::GetPluginsInternal, base::Unretained(this),
514 target_loop, callback),
515 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
516 #elif defined(OS_POSIX)
517 std::vector<webkit::WebPluginInfo> cached_plugins;
518 if (plugin_list_->GetPluginsNoRefresh(&cached_plugins)) {
519 // Can't assume the caller is reentrant.
520 target_loop->PostTask(FROM_HERE,
521 base::Bind(callback, cached_plugins));
522 } else {
523 // If we switch back to loading plugins in process, then we need to make
524 // sure g_thread_init() gets called since plugins may call glib at load.
525 if (!plugin_loader_.get())
526 plugin_loader_ = new PluginLoaderPosix;
527 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
528 base::Bind(&PluginLoaderPosix::LoadPlugins, plugin_loader_,
529 target_loop, callback));
531 #else
532 #error Not implemented
533 #endif
536 #if defined(OS_WIN)
537 void PluginServiceImpl::GetPluginsInternal(
538 base::MessageLoopProxy* target_loop,
539 const PluginService::GetPluginsCallback& callback) {
540 DCHECK(BrowserThread::GetBlockingPool()->IsRunningSequenceOnCurrentThread(
541 plugin_list_token_));
543 std::vector<webkit::WebPluginInfo> plugins;
544 plugin_list_->GetPlugins(&plugins);
546 target_loop->PostTask(FROM_HERE,
547 base::Bind(callback, plugins));
549 #endif
551 void PluginServiceImpl::OnWaitableEventSignaled(
552 base::WaitableEvent* waitable_event) {
553 #if defined(OS_WIN)
554 if (waitable_event == hkcu_event_.get()) {
555 hkcu_key_.StartWatching();
556 } else {
557 hklm_key_.StartWatching();
560 plugin_list_->RefreshPlugins();
561 PurgePluginListCache(NULL, false);
562 #else
563 // This event should only get signaled on a Windows machine.
564 NOTREACHED();
565 #endif // defined(OS_WIN)
568 void PluginServiceImpl::RegisterPepperPlugins() {
569 // TODO(abarth): It seems like the PepperPluginRegistry should do this work.
570 PepperPluginRegistry::ComputeList(&ppapi_plugins_);
571 for (size_t i = 0; i < ppapi_plugins_.size(); ++i) {
572 RegisterInternalPlugin(ppapi_plugins_[i].ToWebPluginInfo(), true);
576 // There should generally be very few plugins so a brute-force search is fine.
577 PepperPluginInfo* PluginServiceImpl::GetRegisteredPpapiPluginInfo(
578 const FilePath& plugin_path) {
579 PepperPluginInfo* info = NULL;
580 for (size_t i = 0; i < ppapi_plugins_.size(); i++) {
581 if (ppapi_plugins_[i].path == plugin_path) {
582 info = &ppapi_plugins_[i];
583 break;
586 if (info)
587 return info;
588 // We did not find the plugin in our list. But wait! the plugin can also
589 // be a latecomer, as it happens with pepper flash. This information
590 // can be obtained from the PluginList singleton and we can use it to
591 // construct it and add it to the list. This same deal needs to be done
592 // in the renderer side in PepperPluginRegistry.
593 webkit::WebPluginInfo webplugin_info;
594 if (!GetPluginInfoByPath(plugin_path, &webplugin_info))
595 return NULL;
596 PepperPluginInfo new_pepper_info;
597 if (!MakePepperPluginInfo(webplugin_info, &new_pepper_info))
598 return NULL;
599 ppapi_plugins_.push_back(new_pepper_info);
600 return &ppapi_plugins_[ppapi_plugins_.size() - 1];
603 #if defined(OS_POSIX) && !defined(OS_OPENBSD) && !defined(OS_ANDROID)
604 // static
605 void PluginServiceImpl::RegisterFilePathWatcher(
606 FilePathWatcher* watcher,
607 const FilePath& path,
608 FilePathWatcher::Delegate* delegate) {
609 bool result = watcher->Watch(path, delegate);
610 DCHECK(result);
612 #endif
614 void PluginServiceImpl::SetFilter(PluginServiceFilter* filter) {
615 filter_ = filter;
618 PluginServiceFilter* PluginServiceImpl::GetFilter() {
619 return filter_;
622 void PluginServiceImpl::ForcePluginShutdown(const FilePath& plugin_path) {
623 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
624 BrowserThread::PostTask(
625 BrowserThread::IO, FROM_HERE,
626 base::Bind(&PluginServiceImpl::ForcePluginShutdown,
627 base::Unretained(this), plugin_path));
628 return;
631 PluginProcessHost* plugin = FindNpapiPluginProcess(plugin_path);
632 if (plugin)
633 plugin->ForceShutdown();
636 static const unsigned int kMaxCrashesPerInterval = 3;
637 static const unsigned int kCrashesInterval = 120;
639 void PluginServiceImpl::RegisterPluginCrash(const FilePath& path) {
640 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
641 std::map<FilePath, std::vector<base::Time> >::iterator i =
642 crash_times_.find(path);
643 if (i == crash_times_.end()) {
644 crash_times_[path] = std::vector<base::Time>();
645 i = crash_times_.find(path);
647 if (i->second.size() == kMaxCrashesPerInterval) {
648 i->second.erase(i->second.begin());
650 base::Time time = base::Time::Now();
651 i->second.push_back(time);
654 bool PluginServiceImpl::IsPluginUnstable(const FilePath& path) {
655 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
656 std::map<FilePath, std::vector<base::Time> >::const_iterator i =
657 crash_times_.find(path);
658 if (i == crash_times_.end()) {
659 return false;
661 if (i->second.size() != kMaxCrashesPerInterval) {
662 return false;
664 base::TimeDelta delta = base::Time::Now() - i->second[0];
665 if (delta.InSeconds() <= kCrashesInterval) {
666 return true;
668 return false;
671 void PluginServiceImpl::RefreshPlugins() {
672 plugin_list_->RefreshPlugins();
675 void PluginServiceImpl::AddExtraPluginPath(const FilePath& path) {
676 plugin_list_->AddExtraPluginPath(path);
679 void PluginServiceImpl::AddExtraPluginDir(const FilePath& path) {
680 plugin_list_->AddExtraPluginDir(path);
683 void PluginServiceImpl::RemoveExtraPluginPath(const FilePath& path) {
684 plugin_list_->RemoveExtraPluginPath(path);
687 void PluginServiceImpl::UnregisterInternalPlugin(const FilePath& path) {
688 plugin_list_->UnregisterInternalPlugin(path);
691 void PluginServiceImpl::SetPluginListForTesting(
692 webkit::npapi::PluginList* plugin_list) {
693 plugin_list_ = plugin_list;
696 #if defined(OS_MACOSX)
697 void PluginServiceImpl::AppActivated() {
698 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
699 base::Bind(&NotifyPluginsOfActivation));
701 #endif
703 void PluginServiceImpl::RegisterInternalPlugin(
704 const webkit::WebPluginInfo& info,
705 bool add_at_beginning) {
706 plugin_list_->RegisterInternalPlugin(info, add_at_beginning);
709 void PluginServiceImpl::GetInternalPlugins(
710 std::vector<webkit::WebPluginInfo>* plugins) {
711 plugin_list_->GetInternalPlugins(plugins);
714 webkit::npapi::PluginList* PluginServiceImpl::GetPluginList() {
715 return plugin_list_;
718 } // namespace content