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. 4 5 #include "content/browser/plugin_service_impl.h" 6 7 #include "base/bind.h" 8 #include "base/command_line.h" 9 #include "base/compiler_specific.h" 10 #include "base/files/file_path.h" 11 #include "base/message_loop/message_loop.h" 12 #include "base/message_loop/message_loop_proxy.h" 13 #include "base/metrics/histogram.h" 14 #include "base/path_service.h" 15 #include "base/strings/string_util.h" 16 #include "base/strings/utf_string_conversions.h" 17 #include "base/synchronization/waitable_event.h" 18 #include "base/threading/thread.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_list.h" 23 #include "content/common/plugin_list.h" 24 #include "content/common/view_messages.h" 25 #include "content/public/browser/browser_thread.h" 26 #include "content/public/browser/content_browser_client.h" 27 #include "content/public/browser/plugin_service_filter.h" 28 #include "content/public/browser/resource_context.h" 29 #include "content/public/common/content_constants.h" 30 #include "content/public/common/content_switches.h" 31 #include "content/public/common/process_type.h" 32 #include "content/public/common/webplugininfo.h" 33 34 #if defined(OS_WIN) 35 #include "content/common/plugin_constants_win.h" 36 #include "ui/gfx/win/hwnd_util.h" 37 #endif 38 39 #if defined(OS_POSIX) 40 #include "content/browser/plugin_loader_posix.h" 41 #endif 42 43 #if defined(OS_POSIX) && !defined(OS_OPENBSD) && !defined(OS_ANDROID) 44 using ::base::FilePathWatcher; 45 #endif 46 47 namespace content { 48 namespace { 49 50 // This enum is used to collect Flash usage data. 51 enum FlashUsage { 52 // Number of browser processes that have started at least one NPAPI Flash 53 // process during their lifetime. 54 START_NPAPI_FLASH_AT_LEAST_ONCE, 55 // Number of browser processes that have started at least one PPAPI Flash 56 // process during their lifetime. 57 START_PPAPI_FLASH_AT_LEAST_ONCE, 58 // Total number of browser processes. 59 TOTAL_BROWSER_PROCESSES, 60 FLASH_USAGE_ENUM_COUNT 61 }; 62 63 bool LoadPluginListInProcess() { 64 #if defined(OS_WIN) 65 return true; 66 #else 67 // If on POSIX, we don't want to load the list of NPAPI plugins in-process as 68 // that causes instability. 69 70 // Can't load the plugins on the utility thread when in single process mode 71 // since that requires GTK which can only be used on the main thread. 72 if (RenderProcessHost::run_renderer_in_process()) 73 return true; 74 75 return !PluginService::GetInstance()->NPAPIPluginsSupported(); 76 #endif 77 } 78 79 // Callback set on the PluginList to assert that plugin loading happens on the 80 // correct thread. 81 void WillLoadPluginsCallback( 82 base::SequencedWorkerPool::SequenceToken token) { 83 if (LoadPluginListInProcess()) { 84 CHECK(BrowserThread::GetBlockingPool()->IsRunningSequenceOnCurrentThread( 85 token)); 86 } else { 87 CHECK(false) << "Plugin loading should happen out-of-process."; 88 } 89 } 90 91 #if defined(OS_MACOSX) 92 void NotifyPluginsOfActivation() { 93 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 94 95 for (PluginProcessHostIterator iter; !iter.Done(); ++iter) 96 iter->OnAppActivation(); 97 } 98 #endif 99 100 #if defined(OS_POSIX) && !defined(OS_OPENBSD) && !defined(OS_ANDROID) 101 void NotifyPluginDirChanged(const base::FilePath& path, bool error) { 102 if (error) { 103 // TODO(pastarmovj): Add some sensible error handling. Maybe silently 104 // stopping the watcher would be enough. Or possibly restart it. 105 NOTREACHED(); 106 return; 107 } 108 VLOG(1) << "Watched path changed: " << path.value(); 109 // Make the plugin list update itself 110 PluginList::Singleton()->RefreshPlugins(); 111 BrowserThread::PostTask( 112 BrowserThread::UI, FROM_HERE, 113 base::Bind(&PluginService::PurgePluginListCache, 114 static_cast<BrowserContext*>(NULL), false)); 115 } 116 #endif 117 118 } // namespace 119 120 // static 121 PluginService* PluginService::GetInstance() { 122 return PluginServiceImpl::GetInstance(); 123 } 124 125 void PluginService::PurgePluginListCache(BrowserContext* browser_context, 126 bool reload_pages) { 127 for (RenderProcessHost::iterator it = RenderProcessHost::AllHostsIterator(); 128 !it.IsAtEnd(); it.Advance()) { 129 RenderProcessHost* host = it.GetCurrentValue(); 130 if (!browser_context || host->GetBrowserContext() == browser_context) 131 host->Send(new ViewMsg_PurgePluginListCache(reload_pages)); 132 } 133 } 134 135 // static 136 PluginServiceImpl* PluginServiceImpl::GetInstance() { 137 return Singleton<PluginServiceImpl>::get(); 138 } 139 140 PluginServiceImpl::PluginServiceImpl() 141 : filter_(NULL) { 142 // Collect the total number of browser processes (which create 143 // PluginServiceImpl objects, to be precise). The number is used to normalize 144 // the number of processes which start at least one NPAPI/PPAPI Flash process. 145 static bool counted = false; 146 if (!counted) { 147 counted = true; 148 UMA_HISTOGRAM_ENUMERATION("Plugin.FlashUsage", TOTAL_BROWSER_PROCESSES, 149 FLASH_USAGE_ENUM_COUNT); 150 } 151 } 152 153 PluginServiceImpl::~PluginServiceImpl() { 154 #if defined(OS_WIN) 155 // Release the events since they're owned by RegKey, not WaitableEvent. 156 hkcu_watcher_.StopWatching(); 157 hklm_watcher_.StopWatching(); 158 if (hkcu_event_) 159 hkcu_event_->Release(); 160 if (hklm_event_) 161 hklm_event_->Release(); 162 #endif 163 // Make sure no plugin channel requests have been leaked. 164 DCHECK(pending_plugin_clients_.empty()); 165 } 166 167 void PluginServiceImpl::Init() { 168 plugin_list_token_ = BrowserThread::GetBlockingPool()->GetSequenceToken(); 169 PluginList::Singleton()->set_will_load_plugins_callback( 170 base::Bind(&WillLoadPluginsCallback, plugin_list_token_)); 171 172 RegisterPepperPlugins(); 173 174 // Load any specified on the command line as well. 175 const CommandLine* command_line = CommandLine::ForCurrentProcess(); 176 base::FilePath path = 177 command_line->GetSwitchValuePath(switches::kLoadPlugin); 178 if (!path.empty()) 179 AddExtraPluginPath(path); 180 path = command_line->GetSwitchValuePath(switches::kExtraPluginDir); 181 if (!path.empty()) 182 PluginList::Singleton()->AddExtraPluginDir(path); 183 184 if (command_line->HasSwitch(switches::kDisablePluginsDiscovery)) 185 PluginList::Singleton()->DisablePluginsDiscovery(); 186 } 187 188 void PluginServiceImpl::StartWatchingPlugins() { 189 // Start watching for changes in the plugin list. This means watching 190 // for changes in the Windows registry keys and on both Windows and POSIX 191 // watch for changes in the paths that are expected to contain plugins. 192 #if defined(OS_WIN) 193 if (hkcu_key_.Create(HKEY_CURRENT_USER, 194 kRegistryMozillaPlugins, 195 KEY_NOTIFY) == ERROR_SUCCESS) { 196 if (hkcu_key_.StartWatching() == ERROR_SUCCESS) { 197 hkcu_event_.reset(new base::WaitableEvent(hkcu_key_.watch_event())); 198 base::WaitableEventWatcher::EventCallback callback = 199 base::Bind(&PluginServiceImpl::OnWaitableEventSignaled, 200 base::Unretained(this)); 201 hkcu_watcher_.StartWatching(hkcu_event_.get(), callback); 202 } 203 } 204 if (hklm_key_.Create(HKEY_LOCAL_MACHINE, 205 kRegistryMozillaPlugins, 206 KEY_NOTIFY) == ERROR_SUCCESS) { 207 if (hklm_key_.StartWatching() == ERROR_SUCCESS) { 208 hklm_event_.reset(new base::WaitableEvent(hklm_key_.watch_event())); 209 base::WaitableEventWatcher::EventCallback callback = 210 base::Bind(&PluginServiceImpl::OnWaitableEventSignaled, 211 base::Unretained(this)); 212 hklm_watcher_.StartWatching(hklm_event_.get(), callback); 213 } 214 } 215 #endif 216 #if defined(OS_POSIX) && !defined(OS_OPENBSD) && !defined(OS_ANDROID) 217 // On ChromeOS the user can't install plugins anyway and on Windows all 218 // important plugins register themselves in the registry so no need to do that. 219 220 // Get the list of all paths for registering the FilePathWatchers 221 // that will track and if needed reload the list of plugins on runtime. 222 std::vector<base::FilePath> plugin_dirs; 223 PluginList::Singleton()->GetPluginDirectories(&plugin_dirs); 224 225 for (size_t i = 0; i < plugin_dirs.size(); ++i) { 226 // FilePathWatcher can not handle non-absolute paths under windows. 227 // We don't watch for file changes in windows now but if this should ever 228 // be extended to Windows these lines might save some time of debugging. 229 #if defined(OS_WIN) 230 if (!plugin_dirs[i].IsAbsolute()) 231 continue; 232 #endif 233 FilePathWatcher* watcher = new FilePathWatcher(); 234 VLOG(1) << "Watching for changes in: " << plugin_dirs[i].value(); 235 BrowserThread::PostTask( 236 BrowserThread::FILE, FROM_HERE, 237 base::Bind(&PluginServiceImpl::RegisterFilePathWatcher, watcher, 238 plugin_dirs[i])); 239 file_watchers_.push_back(watcher); 240 } 241 #endif 242 } 243 244 PluginProcessHost* PluginServiceImpl::FindNpapiPluginProcess( 245 const base::FilePath& plugin_path) { 246 for (PluginProcessHostIterator iter; !iter.Done(); ++iter) { 247 if (iter->info().path == plugin_path) 248 return *iter; 249 } 250 251 return NULL; 252 } 253 254 PpapiPluginProcessHost* PluginServiceImpl::FindPpapiPluginProcess( 255 const base::FilePath& plugin_path, 256 const base::FilePath& profile_data_directory) { 257 for (PpapiPluginProcessHostIterator iter; !iter.Done(); ++iter) { 258 if (iter->plugin_path() == plugin_path && 259 iter->profile_data_directory() == profile_data_directory) { 260 return *iter; 261 } 262 } 263 return NULL; 264 } 265 266 PpapiPluginProcessHost* PluginServiceImpl::FindPpapiBrokerProcess( 267 const base::FilePath& broker_path) { 268 for (PpapiBrokerProcessHostIterator iter; !iter.Done(); ++iter) { 269 if (iter->plugin_path() == broker_path) 270 return *iter; 271 } 272 273 return NULL; 274 } 275 276 PluginProcessHost* PluginServiceImpl::FindOrStartNpapiPluginProcess( 277 int render_process_id, 278 const base::FilePath& plugin_path) { 279 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 280 281 if (filter_ && !filter_->CanLoadPlugin(render_process_id, plugin_path)) 282 return NULL; 283 284 PluginProcessHost* plugin_host = FindNpapiPluginProcess(plugin_path); 285 if (plugin_host) 286 return plugin_host; 287 288 WebPluginInfo info; 289 if (!GetPluginInfoByPath(plugin_path, &info)) { 290 return NULL; 291 } 292 293 // Record when NPAPI Flash process is started for the first time. 294 static bool counted = false; 295 if (!counted && UTF16ToUTF8(info.name) == kFlashPluginName) { 296 counted = true; 297 UMA_HISTOGRAM_ENUMERATION("Plugin.FlashUsage", 298 START_NPAPI_FLASH_AT_LEAST_ONCE, 299 FLASH_USAGE_ENUM_COUNT); 300 } 301 #if defined(OS_CHROMEOS) 302 // TODO(ihf): Move to an earlier place once crbug.com/314301 is fixed. For now 303 // we still want Plugin.FlashUsage recorded if we end up here. 304 LOG(WARNING) << "Refusing to start npapi plugin on ChromeOS."; 305 return NULL; 306 #endif 307 // This plugin isn't loaded by any plugin process, so create a new process. 308 scoped_ptr<PluginProcessHost> new_host(new PluginProcessHost()); 309 if (!new_host->Init(info)) { 310 NOTREACHED(); // Init is not expected to fail. 311 return NULL; 312 } 313 return new_host.release(); 314 } 315 316 PpapiPluginProcessHost* PluginServiceImpl::FindOrStartPpapiPluginProcess( 317 int render_process_id, 318 const base::FilePath& plugin_path, 319 const base::FilePath& profile_data_directory) { 320 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 321 322 if (filter_ && !filter_->CanLoadPlugin(render_process_id, plugin_path)) { 323 VLOG(1) << "Unable to load ppapi plugin: " << plugin_path.MaybeAsASCII(); 324 return NULL; 325 } 326 327 PpapiPluginProcessHost* plugin_host = 328 FindPpapiPluginProcess(plugin_path, profile_data_directory); 329 if (plugin_host) 330 return plugin_host; 331 332 // Validate that the plugin is actually registered. 333 PepperPluginInfo* info = GetRegisteredPpapiPluginInfo(plugin_path); 334 if (!info) { 335 VLOG(1) << "Unable to find ppapi plugin registration for: " 336 << plugin_path.MaybeAsASCII(); 337 return NULL; 338 } 339 340 // Record when PPAPI Flash process is started for the first time. 341 static bool counted = false; 342 if (!counted && info->name == kFlashPluginName) { 343 counted = true; 344 UMA_HISTOGRAM_ENUMERATION("Plugin.FlashUsage", 345 START_PPAPI_FLASH_AT_LEAST_ONCE, 346 FLASH_USAGE_ENUM_COUNT); 347 } 348 349 // This plugin isn't loaded by any plugin process, so create a new process. 350 plugin_host = PpapiPluginProcessHost::CreatePluginHost( 351 *info, profile_data_directory); 352 if (!plugin_host) { 353 VLOG(1) << "Unable to create ppapi plugin process for: " 354 << plugin_path.MaybeAsASCII(); 355 } 356 357 return plugin_host; 358 } 359 360 PpapiPluginProcessHost* PluginServiceImpl::FindOrStartPpapiBrokerProcess( 361 int render_process_id, 362 const base::FilePath& plugin_path) { 363 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 364 365 if (filter_ && !filter_->CanLoadPlugin(render_process_id, plugin_path)) 366 return NULL; 367 368 PpapiPluginProcessHost* plugin_host = FindPpapiBrokerProcess(plugin_path); 369 if (plugin_host) 370 return plugin_host; 371 372 // Validate that the plugin is actually registered. 373 PepperPluginInfo* info = GetRegisteredPpapiPluginInfo(plugin_path); 374 if (!info) 375 return NULL; 376 377 // TODO(ddorwin): Uncomment once out of process is supported. 378 // DCHECK(info->is_out_of_process); 379 380 // This broker isn't loaded by any broker process, so create a new process. 381 return PpapiPluginProcessHost::CreateBrokerHost(*info); 382 } 383 384 void PluginServiceImpl::OpenChannelToNpapiPlugin( 385 int render_process_id, 386 int render_frame_id, 387 const GURL& url, 388 const GURL& page_url, 389 const std::string& mime_type, 390 PluginProcessHost::Client* client) { 391 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 392 DCHECK(!ContainsKey(pending_plugin_clients_, client)); 393 pending_plugin_clients_.insert(client); 394 395 // Make sure plugins are loaded if necessary. 396 PluginServiceFilterParams params = { 397 render_process_id, 398 render_frame_id, 399 page_url, 400 client->GetResourceContext() 401 }; 402 GetPlugins(base::Bind( 403 &PluginServiceImpl::ForwardGetAllowedPluginForOpenChannelToPlugin, 404 base::Unretained(this), params, url, mime_type, client)); 405 } 406 407 void PluginServiceImpl::OpenChannelToPpapiPlugin( 408 int render_process_id, 409 const base::FilePath& plugin_path, 410 const base::FilePath& profile_data_directory, 411 PpapiPluginProcessHost::PluginClient* client) { 412 PpapiPluginProcessHost* plugin_host = FindOrStartPpapiPluginProcess( 413 render_process_id, plugin_path, profile_data_directory); 414 if (plugin_host) { 415 plugin_host->OpenChannelToPlugin(client); 416 } else { 417 // Send error. 418 client->OnPpapiChannelOpened(IPC::ChannelHandle(), base::kNullProcessId, 0); 419 } 420 } 421 422 void PluginServiceImpl::OpenChannelToPpapiBroker( 423 int render_process_id, 424 const base::FilePath& path, 425 PpapiPluginProcessHost::BrokerClient* client) { 426 PpapiPluginProcessHost* plugin_host = FindOrStartPpapiBrokerProcess( 427 render_process_id, path); 428 if (plugin_host) { 429 plugin_host->OpenChannelToPlugin(client); 430 } else { 431 // Send error. 432 client->OnPpapiChannelOpened(IPC::ChannelHandle(), base::kNullProcessId, 0); 433 } 434 } 435 436 void PluginServiceImpl::CancelOpenChannelToNpapiPlugin( 437 PluginProcessHost::Client* client) { 438 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 439 DCHECK(ContainsKey(pending_plugin_clients_, client)); 440 pending_plugin_clients_.erase(client); 441 } 442 443 void PluginServiceImpl::ForwardGetAllowedPluginForOpenChannelToPlugin( 444 const PluginServiceFilterParams& params, 445 const GURL& url, 446 const std::string& mime_type, 447 PluginProcessHost::Client* client, 448 const std::vector<WebPluginInfo>&) { 449 GetAllowedPluginForOpenChannelToPlugin( 450 params.render_process_id, params.render_frame_id, url, params.page_url, 451 mime_type, client, params.resource_context); 452 } 453 454 void PluginServiceImpl::GetAllowedPluginForOpenChannelToPlugin( 455 int render_process_id, 456 int render_frame_id, 457 const GURL& url, 458 const GURL& page_url, 459 const std::string& mime_type, 460 PluginProcessHost::Client* client, 461 ResourceContext* resource_context) { 462 WebPluginInfo info; 463 bool allow_wildcard = true; 464 bool found = GetPluginInfo( 465 render_process_id, render_frame_id, resource_context, 466 url, page_url, mime_type, allow_wildcard, 467 NULL, &info, NULL); 468 base::FilePath plugin_path; 469 if (found) 470 plugin_path = info.path; 471 472 // Now we jump back to the IO thread to finish opening the channel. 473 BrowserThread::PostTask( 474 BrowserThread::IO, FROM_HERE, 475 base::Bind(&PluginServiceImpl::FinishOpenChannelToPlugin, 476 base::Unretained(this), 477 render_process_id, 478 plugin_path, 479 client)); 480 } 481 482 void PluginServiceImpl::FinishOpenChannelToPlugin( 483 int render_process_id, 484 const base::FilePath& plugin_path, 485 PluginProcessHost::Client* client) { 486 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 487 488 // Make sure it hasn't been canceled yet. 489 if (!ContainsKey(pending_plugin_clients_, client)) 490 return; 491 pending_plugin_clients_.erase(client); 492 493 PluginProcessHost* plugin_host = FindOrStartNpapiPluginProcess( 494 render_process_id, plugin_path); 495 if (plugin_host) { 496 client->OnFoundPluginProcessHost(plugin_host); 497 plugin_host->OpenChannelToPlugin(client); 498 } else { 499 client->OnError(); 500 } 501 } 502 503 bool PluginServiceImpl::GetPluginInfoArray( 504 const GURL& url, 505 const std::string& mime_type, 506 bool allow_wildcard, 507 std::vector<WebPluginInfo>* plugins, 508 std::vector<std::string>* actual_mime_types) { 509 bool use_stale = false; 510 PluginList::Singleton()->GetPluginInfoArray( 511 url, mime_type, allow_wildcard, &use_stale, NPAPIPluginsSupported(), 512 plugins, actual_mime_types); 513 return use_stale; 514 } 515 516 bool PluginServiceImpl::GetPluginInfo(int render_process_id, 517 int render_frame_id, 518 ResourceContext* context, 519 const GURL& url, 520 const GURL& page_url, 521 const std::string& mime_type, 522 bool allow_wildcard, 523 bool* is_stale, 524 WebPluginInfo* info, 525 std::string* actual_mime_type) { 526 std::vector<WebPluginInfo> plugins; 527 std::vector<std::string> mime_types; 528 bool stale = GetPluginInfoArray( 529 url, mime_type, allow_wildcard, &plugins, &mime_types); 530 if (is_stale) 531 *is_stale = stale; 532 533 for (size_t i = 0; i < plugins.size(); ++i) { 534 if (!filter_ || filter_->IsPluginAvailable(render_process_id, 535 render_frame_id, 536 context, 537 url, 538 page_url, 539 &plugins[i])) { 540 *info = plugins[i]; 541 if (actual_mime_type) 542 *actual_mime_type = mime_types[i]; 543 return true; 544 } 545 } 546 return false; 547 } 548 549 bool PluginServiceImpl::GetPluginInfoByPath(const base::FilePath& plugin_path, 550 WebPluginInfo* info) { 551 std::vector<WebPluginInfo> plugins; 552 PluginList::Singleton()->GetPluginsNoRefresh(&plugins); 553 554 for (std::vector<WebPluginInfo>::iterator it = plugins.begin(); 555 it != plugins.end(); 556 ++it) { 557 if (it->path == plugin_path) { 558 *info = *it; 559 return true; 560 } 561 } 562 563 return false; 564 } 565 566 base::string16 PluginServiceImpl::GetPluginDisplayNameByPath( 567 const base::FilePath& path) { 568 base::string16 plugin_name = path.LossyDisplayName(); 569 WebPluginInfo info; 570 if (PluginService::GetInstance()->GetPluginInfoByPath(path, &info) && 571 !info.name.empty()) { 572 plugin_name = info.name; 573 #if defined(OS_MACOSX) 574 // Many plugins on the Mac have .plugin in the actual name, which looks 575 // terrible, so look for that and strip it off if present. 576 const std::string kPluginExtension = ".plugin"; 577 if (EndsWith(plugin_name, ASCIIToUTF16(kPluginExtension), true)) 578 plugin_name.erase(plugin_name.length() - kPluginExtension.length()); 579 #endif // OS_MACOSX 580 } 581 return plugin_name; 582 } 583 584 void PluginServiceImpl::GetPlugins(const GetPluginsCallback& callback) { 585 scoped_refptr<base::MessageLoopProxy> target_loop( 586 base::MessageLoop::current()->message_loop_proxy()); 587 588 if (LoadPluginListInProcess()) { 589 BrowserThread::GetBlockingPool()-> 590 PostSequencedWorkerTaskWithShutdownBehavior( 591 plugin_list_token_, 592 FROM_HERE, 593 base::Bind(&PluginServiceImpl::GetPluginsInternal, 594 base::Unretained(this), 595 target_loop, callback), 596 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN); 597 return; 598 } 599 #if defined(OS_POSIX) 600 std::vector<WebPluginInfo> cached_plugins; 601 if (PluginList::Singleton()->GetPluginsNoRefresh(&cached_plugins)) { 602 // Can't assume the caller is reentrant. 603 target_loop->PostTask(FROM_HERE, 604 base::Bind(callback, cached_plugins)); 605 } else { 606 // If we switch back to loading plugins in process, then we need to make 607 // sure g_thread_init() gets called since plugins may call glib at load. 608 if (!plugin_loader_.get()) 609 plugin_loader_ = new PluginLoaderPosix; 610 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, 611 base::Bind(&PluginLoaderPosix::LoadPlugins, plugin_loader_, 612 target_loop, callback)); 613 } 614 #else 615 NOTREACHED(); 616 #endif 617 } 618 619 void PluginServiceImpl::GetPluginsInternal( 620 base::MessageLoopProxy* target_loop, 621 const PluginService::GetPluginsCallback& callback) { 622 DCHECK(BrowserThread::GetBlockingPool()->IsRunningSequenceOnCurrentThread( 623 plugin_list_token_)); 624 625 std::vector<WebPluginInfo> plugins; 626 PluginList::Singleton()->GetPlugins(&plugins, NPAPIPluginsSupported()); 627 628 target_loop->PostTask(FROM_HERE, 629 base::Bind(callback, plugins)); 630 } 631 632 void PluginServiceImpl::OnWaitableEventSignaled( 633 base::WaitableEvent* waitable_event) { 634 #if defined(OS_WIN) 635 if (waitable_event == hkcu_event_) { 636 hkcu_key_.StartWatching(); 637 } else { 638 hklm_key_.StartWatching(); 639 } 640 641 PluginList::Singleton()->RefreshPlugins(); 642 PurgePluginListCache(NULL, false); 643 #else 644 // This event should only get signaled on a Windows machine. 645 NOTREACHED(); 646 #endif // defined(OS_WIN) 647 } 648 649 void PluginServiceImpl::RegisterPepperPlugins() { 650 ComputePepperPluginList(&ppapi_plugins_); 651 for (size_t i = 0; i < ppapi_plugins_.size(); ++i) { 652 RegisterInternalPlugin(ppapi_plugins_[i].ToWebPluginInfo(), true); 653 } 654 } 655 656 // There should generally be very few plugins so a brute-force search is fine. 657 PepperPluginInfo* PluginServiceImpl::GetRegisteredPpapiPluginInfo( 658 const base::FilePath& plugin_path) { 659 PepperPluginInfo* info = NULL; 660 for (size_t i = 0; i < ppapi_plugins_.size(); ++i) { 661 if (ppapi_plugins_[i].path == plugin_path) { 662 info = &ppapi_plugins_[i]; 663 break; 664 } 665 } 666 if (info) 667 return info; 668 // We did not find the plugin in our list. But wait! the plugin can also 669 // be a latecomer, as it happens with pepper flash. This information 670 // can be obtained from the PluginList singleton and we can use it to 671 // construct it and add it to the list. This same deal needs to be done 672 // in the renderer side in PepperPluginRegistry. 673 WebPluginInfo webplugin_info; 674 if (!GetPluginInfoByPath(plugin_path, &webplugin_info)) 675 return NULL; 676 PepperPluginInfo new_pepper_info; 677 if (!MakePepperPluginInfo(webplugin_info, &new_pepper_info)) 678 return NULL; 679 ppapi_plugins_.push_back(new_pepper_info); 680 return &ppapi_plugins_[ppapi_plugins_.size() - 1]; 681 } 682 683 #if defined(OS_POSIX) && !defined(OS_OPENBSD) && !defined(OS_ANDROID) 684 // static 685 void PluginServiceImpl::RegisterFilePathWatcher(FilePathWatcher* watcher, 686 const base::FilePath& path) { 687 bool result = watcher->Watch(path, false, 688 base::Bind(&NotifyPluginDirChanged)); 689 DCHECK(result); 690 } 691 #endif 692 693 void PluginServiceImpl::SetFilter(PluginServiceFilter* filter) { 694 filter_ = filter; 695 } 696 697 PluginServiceFilter* PluginServiceImpl::GetFilter() { 698 return filter_; 699 } 700 701 void PluginServiceImpl::ForcePluginShutdown(const base::FilePath& plugin_path) { 702 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { 703 BrowserThread::PostTask( 704 BrowserThread::IO, FROM_HERE, 705 base::Bind(&PluginServiceImpl::ForcePluginShutdown, 706 base::Unretained(this), plugin_path)); 707 return; 708 } 709 710 PluginProcessHost* plugin = FindNpapiPluginProcess(plugin_path); 711 if (plugin) 712 plugin->ForceShutdown(); 713 } 714 715 static const unsigned int kMaxCrashesPerInterval = 3; 716 static const unsigned int kCrashesInterval = 120; 717 718 void PluginServiceImpl::RegisterPluginCrash(const base::FilePath& path) { 719 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 720 std::map<base::FilePath, std::vector<base::Time> >::iterator i = 721 crash_times_.find(path); 722 if (i == crash_times_.end()) { 723 crash_times_[path] = std::vector<base::Time>(); 724 i = crash_times_.find(path); 725 } 726 if (i->second.size() == kMaxCrashesPerInterval) { 727 i->second.erase(i->second.begin()); 728 } 729 base::Time time = base::Time::Now(); 730 i->second.push_back(time); 731 } 732 733 bool PluginServiceImpl::IsPluginUnstable(const base::FilePath& path) { 734 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 735 std::map<base::FilePath, std::vector<base::Time> >::const_iterator i = 736 crash_times_.find(path); 737 if (i == crash_times_.end()) { 738 return false; 739 } 740 if (i->second.size() != kMaxCrashesPerInterval) { 741 return false; 742 } 743 base::TimeDelta delta = base::Time::Now() - i->second[0]; 744 return delta.InSeconds() <= kCrashesInterval; 745 } 746 747 void PluginServiceImpl::RefreshPlugins() { 748 PluginList::Singleton()->RefreshPlugins(); 749 } 750 751 void PluginServiceImpl::AddExtraPluginPath(const base::FilePath& path) { 752 if (!NPAPIPluginsSupported()) { 753 // TODO(jam): remove and just have CHECK once we're sure this doesn't get 754 // triggered. 755 DVLOG(0) << "NPAPI plugins not supported"; 756 return; 757 } 758 PluginList::Singleton()->AddExtraPluginPath(path); 759 } 760 761 void PluginServiceImpl::RemoveExtraPluginPath(const base::FilePath& path) { 762 PluginList::Singleton()->RemoveExtraPluginPath(path); 763 } 764 765 void PluginServiceImpl::AddExtraPluginDir(const base::FilePath& path) { 766 PluginList::Singleton()->AddExtraPluginDir(path); 767 } 768 769 void PluginServiceImpl::RegisterInternalPlugin( 770 const WebPluginInfo& info, 771 bool add_at_beginning) { 772 if (!NPAPIPluginsSupported() && 773 info.type == WebPluginInfo::PLUGIN_TYPE_NPAPI) { 774 DVLOG(0) << "Don't register NPAPI plugins when they're not supported"; 775 return; 776 } 777 PluginList::Singleton()->RegisterInternalPlugin(info, add_at_beginning); 778 } 779 780 void PluginServiceImpl::UnregisterInternalPlugin(const base::FilePath& path) { 781 PluginList::Singleton()->UnregisterInternalPlugin(path); 782 } 783 784 void PluginServiceImpl::GetInternalPlugins( 785 std::vector<WebPluginInfo>* plugins) { 786 PluginList::Singleton()->GetInternalPlugins(plugins); 787 } 788 789 bool PluginServiceImpl::NPAPIPluginsSupported() { 790 #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_BSD) || \ 791 (defined(OS_LINUX) && !defined(USE_AURA)) 792 return true; 793 #else 794 return false; 795 #endif 796 } 797 798 void PluginServiceImpl::DisablePluginsDiscoveryForTesting() { 799 PluginList::Singleton()->DisablePluginsDiscovery(); 800 } 801 802 #if defined(OS_MACOSX) 803 void PluginServiceImpl::AppActivated() { 804 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, 805 base::Bind(&NotifyPluginsOfActivation)); 806 } 807 #elif defined(OS_WIN) 808 809 bool GetPluginPropertyFromWindow( 810 HWND window, const wchar_t* plugin_atom_property, 811 base::string16* plugin_property) { 812 ATOM plugin_atom = reinterpret_cast<ATOM>( 813 GetPropW(window, plugin_atom_property)); 814 if (plugin_atom != 0) { 815 WCHAR plugin_property_local[MAX_PATH] = {0}; 816 GlobalGetAtomNameW(plugin_atom, 817 plugin_property_local, 818 ARRAYSIZE(plugin_property_local)); 819 *plugin_property = plugin_property_local; 820 return true; 821 } 822 return false; 823 } 824 825 bool PluginServiceImpl::GetPluginInfoFromWindow( 826 HWND window, 827 base::string16* plugin_name, 828 base::string16* plugin_version) { 829 if (!IsPluginWindow(window)) 830 return false; 831 832 GetPluginPropertyFromWindow( 833 window, kPluginNameAtomProperty, plugin_name); 834 GetPluginPropertyFromWindow( 835 window, kPluginVersionAtomProperty, plugin_version); 836 return true; 837 } 838 839 bool PluginServiceImpl::IsPluginWindow(HWND window) { 840 return gfx::GetClassName(window) == base::string16(kNativeWindowClassName); 841 } 842 #endif 843 844 bool PluginServiceImpl::PpapiDevChannelSupported() { 845 return content::GetContentClient()->browser()-> 846 IsPluginAllowedToUseDevChannelAPIs(); 847 } 848 849 } // namespace content 850