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"
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"
36 #include "webkit/plugins/npapi/plugin_constants_win.h"
40 #include "content/browser/plugin_loader_posix.h"
43 #if defined(OS_POSIX) && !defined(OS_OPENBSD) && !defined(OS_ANDROID)
44 using ::base::files::FilePathWatcher
;
50 // Callback set on the PluginList to assert that plugin loading happens on the
53 void WillLoadPluginsCallbackWin(
54 base::SequencedWorkerPool::SequenceToken token
) {
55 CHECK(BrowserThread::GetBlockingPool()->IsRunningSequenceOnCurrentThread(
59 void WillLoadPluginsCallbackPosix() {
60 CHECK(false) << "Plugin loading should happen out-of-process.";
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();
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.
94 virtual ~PluginDirWatcherDelegate() {}
99 PluginService
* PluginService::GetInstance() {
100 return PluginServiceImpl::GetInstance();
103 void PluginService::PurgePluginListCache(BrowserContext
* browser_context
,
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
));
114 PluginServiceImpl
* PluginServiceImpl::GetInstance() {
115 return Singleton
<PluginServiceImpl
>::get();
118 PluginServiceImpl::PluginServiceImpl()
119 : plugin_list_(NULL
), filter_(NULL
) {
122 PluginServiceImpl::~PluginServiceImpl() {
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();
132 // Make sure no plugin channel requests have been leaked.
133 DCHECK(pending_plugin_clients_
.empty());
136 void PluginServiceImpl::Init() {
138 plugin_list_
= webkit::npapi::PluginList::Singleton();
141 plugin_list_token_
= BrowserThread::GetBlockingPool()->GetSequenceToken();
142 plugin_list_
->set_will_load_plugins_callback(
143 base::Bind(&WillLoadPluginsCallbackWin
, plugin_list_token_
));
145 plugin_list_
->set_will_load_plugins_callback(
146 base::Bind(&WillLoadPluginsCallbackPosix
));
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
);
157 AddExtraPluginPath(path
);
158 path
= command_line
->GetSwitchValuePath(switches::kExtraPluginDir
);
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.
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);
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.
199 if (!plugin_dirs
[i
].IsAbsolute())
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
);
213 PluginProcessHost
* PluginServiceImpl::FindNpapiPluginProcess(
214 const FilePath
& plugin_path
) {
215 for (PluginProcessHostIterator iter
; !iter
.Done(); ++iter
) {
216 if (iter
->info().path
== plugin_path
)
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
) {
235 PpapiPluginProcessHost
* PluginServiceImpl::FindPpapiBrokerProcess(
236 const FilePath
& broker_path
) {
237 for (PpapiBrokerProcessHostIterator iter
; !iter
.Done(); ++iter
) {
238 if (iter
->plugin_path() == broker_path
)
245 PluginProcessHost
* PluginServiceImpl::FindOrStartNpapiPluginProcess(
246 const FilePath
& plugin_path
) {
247 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
249 PluginProcessHost
* plugin_host
= FindNpapiPluginProcess(plugin_path
);
253 webkit::WebPluginInfo info
;
254 if (!GetPluginInfoByPath(plugin_path
, &info
)) {
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.
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
);
279 // Validate that the plugin is actually registered.
280 PepperPluginInfo
* info
= GetRegisteredPpapiPluginInfo(plugin_path
);
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());
293 PpapiPluginProcessHost
* PluginServiceImpl::FindOrStartPpapiBrokerProcess(
294 const FilePath
& plugin_path
) {
295 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
297 PpapiPluginProcessHost
* plugin_host
= FindPpapiBrokerProcess(plugin_path
);
301 // Validate that the plugin is actually registered.
302 PepperPluginInfo
* info
= GetRegisteredPpapiPluginInfo(plugin_path
);
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
,
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
= {
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
);
343 plugin_host
->OpenChannelToPlugin(client
);
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
);
356 plugin_host
->OpenChannelToPlugin(client
);
359 client
->OnPpapiChannelOpened(IPC::ChannelHandle(), 0);
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
,
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
,
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
,
396 FilePath plugin_path
;
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
))
415 pending_plugin_clients_
.erase(client
);
417 PluginProcessHost
* plugin_host
= FindOrStartNpapiPluginProcess(plugin_path
);
419 client
->OnFoundPluginProcessHost(plugin_host
);
420 plugin_host
->OpenChannelToPlugin(client
);
426 bool PluginServiceImpl::GetPluginInfoArray(
428 const std::string
& mime_type
,
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
);
438 bool PluginServiceImpl::GetPluginInfo(int render_process_id
,
440 ResourceContext
* context
,
442 const GURL
& page_url
,
443 const std::string
& mime_type
,
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
);
455 for (size_t i
= 0; i
< plugins
.size(); ++i
) {
456 if (!filter_
|| filter_
->ShouldUsePlugin(render_process_id
,
463 if (actual_mime_type
)
464 *actual_mime_type
= mime_types
[i
];
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();
479 if (it
->path
== plugin_path
) {
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());
505 void PluginServiceImpl::GetPlugins(const GetPluginsCallback
& callback
) {
506 scoped_refptr
<base::MessageLoopProxy
> target_loop(
507 MessageLoop::current()->message_loop_proxy());
510 BrowserThread::GetBlockingPool()->PostSequencedWorkerTaskWithShutdownBehavior(
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
));
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
));
532 #error Not implemented
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
));
551 void PluginServiceImpl::OnWaitableEventSignaled(
552 base::WaitableEvent
* waitable_event
) {
554 if (waitable_event
== hkcu_event_
.get()) {
555 hkcu_key_
.StartWatching();
557 hklm_key_
.StartWatching();
560 plugin_list_
->RefreshPlugins();
561 PurgePluginListCache(NULL
, false);
563 // This event should only get signaled on a Windows machine.
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
];
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
))
596 PepperPluginInfo new_pepper_info
;
597 if (!MakePepperPluginInfo(webplugin_info
, &new_pepper_info
))
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)
605 void PluginServiceImpl::RegisterFilePathWatcher(
606 FilePathWatcher
* watcher
,
607 const FilePath
& path
,
608 FilePathWatcher::Delegate
* delegate
) {
609 bool result
= watcher
->Watch(path
, delegate
);
614 void PluginServiceImpl::SetFilter(PluginServiceFilter
* filter
) {
618 PluginServiceFilter
* PluginServiceImpl::GetFilter() {
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
));
631 PluginProcessHost
* plugin
= FindNpapiPluginProcess(plugin_path
);
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()) {
661 if (i
->second
.size() != kMaxCrashesPerInterval
) {
664 base::TimeDelta delta
= base::Time::Now() - i
->second
[0];
665 if (delta
.InSeconds() <= kCrashesInterval
) {
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
));
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() {
718 } // namespace content