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/base/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 // The --site-per-process flag enables an out-of-process iframes 175 // prototype, which uses WebView for rendering. We need to register the MIME 176 // type we use with the plugin, so the renderer can instantiate it. 177 const CommandLine* command_line = CommandLine::ForCurrentProcess(); 178 if (command_line->HasSwitch(switches::kSitePerProcess)) { 179 WebPluginInfo webview_plugin( 180 ASCIIToUTF16("WebView Tag"), 181 base::FilePath(), 182 ASCIIToUTF16("1.2.3.4"), 183 ASCIIToUTF16("Browser Plugin.")); 184 webview_plugin.type = WebPluginInfo::PLUGIN_TYPE_NPAPI; 185 WebPluginMimeType webview_plugin_mime_type; 186 webview_plugin_mime_type.mime_type = "application/browser-plugin"; 187 webview_plugin_mime_type.file_extensions.push_back("*"); 188 webview_plugin.mime_types.push_back(webview_plugin_mime_type); 189 RegisterInternalPlugin(webview_plugin, true); 190 } 191 192 // Load any specified on the command line as well. 193 base::FilePath path = 194 command_line->GetSwitchValuePath(switches::kLoadPlugin); 195 if (!path.empty()) 196 AddExtraPluginPath(path); 197 path = command_line->GetSwitchValuePath(switches::kExtraPluginDir); 198 if (!path.empty()) 199 PluginList::Singleton()->AddExtraPluginDir(path); 200 201 if (command_line->HasSwitch(switches::kDisablePluginsDiscovery)) 202 PluginList::Singleton()->DisablePluginsDiscovery(); 203 } 204 205 void PluginServiceImpl::StartWatchingPlugins() { 206 // Start watching for changes in the plugin list. This means watching 207 // for changes in the Windows registry keys and on both Windows and POSIX 208 // watch for changes in the paths that are expected to contain plugins. 209 #if defined(OS_WIN) 210 if (hkcu_key_.Create(HKEY_CURRENT_USER, 211 kRegistryMozillaPlugins, 212 KEY_NOTIFY) == ERROR_SUCCESS) { 213 if (hkcu_key_.StartWatching() == ERROR_SUCCESS) { 214 hkcu_event_.reset(new base::WaitableEvent(hkcu_key_.watch_event())); 215 base::WaitableEventWatcher::EventCallback callback = 216 base::Bind(&PluginServiceImpl::OnWaitableEventSignaled, 217 base::Unretained(this)); 218 hkcu_watcher_.StartWatching(hkcu_event_.get(), callback); 219 } 220 } 221 if (hklm_key_.Create(HKEY_LOCAL_MACHINE, 222 kRegistryMozillaPlugins, 223 KEY_NOTIFY) == ERROR_SUCCESS) { 224 if (hklm_key_.StartWatching() == ERROR_SUCCESS) { 225 hklm_event_.reset(new base::WaitableEvent(hklm_key_.watch_event())); 226 base::WaitableEventWatcher::EventCallback callback = 227 base::Bind(&PluginServiceImpl::OnWaitableEventSignaled, 228 base::Unretained(this)); 229 hklm_watcher_.StartWatching(hklm_event_.get(), callback); 230 } 231 } 232 #endif 233 #if defined(OS_POSIX) && !defined(OS_OPENBSD) && !defined(OS_ANDROID) 234 // On ChromeOS the user can't install plugins anyway and on Windows all 235 // important plugins register themselves in the registry so no need to do that. 236 237 // Get the list of all paths for registering the FilePathWatchers 238 // that will track and if needed reload the list of plugins on runtime. 239 std::vector<base::FilePath> plugin_dirs; 240 PluginList::Singleton()->GetPluginDirectories(&plugin_dirs); 241 242 for (size_t i = 0; i < plugin_dirs.size(); ++i) { 243 // FilePathWatcher can not handle non-absolute paths under windows. 244 // We don't watch for file changes in windows now but if this should ever 245 // be extended to Windows these lines might save some time of debugging. 246 #if defined(OS_WIN) 247 if (!plugin_dirs[i].IsAbsolute()) 248 continue; 249 #endif 250 FilePathWatcher* watcher = new FilePathWatcher(); 251 VLOG(1) << "Watching for changes in: " << plugin_dirs[i].value(); 252 BrowserThread::PostTask( 253 BrowserThread::FILE, FROM_HERE, 254 base::Bind(&PluginServiceImpl::RegisterFilePathWatcher, watcher, 255 plugin_dirs[i])); 256 file_watchers_.push_back(watcher); 257 } 258 #endif 259 } 260 261 PluginProcessHost* PluginServiceImpl::FindNpapiPluginProcess( 262 const base::FilePath& plugin_path) { 263 for (PluginProcessHostIterator iter; !iter.Done(); ++iter) { 264 if (iter->info().path == plugin_path) 265 return *iter; 266 } 267 268 return NULL; 269 } 270 271 PpapiPluginProcessHost* PluginServiceImpl::FindPpapiPluginProcess( 272 const base::FilePath& plugin_path, 273 const base::FilePath& profile_data_directory) { 274 for (PpapiPluginProcessHostIterator iter; !iter.Done(); ++iter) { 275 if (iter->plugin_path() == plugin_path && 276 iter->profile_data_directory() == profile_data_directory) { 277 return *iter; 278 } 279 } 280 return NULL; 281 } 282 283 PpapiPluginProcessHost* PluginServiceImpl::FindPpapiBrokerProcess( 284 const base::FilePath& broker_path) { 285 for (PpapiBrokerProcessHostIterator iter; !iter.Done(); ++iter) { 286 if (iter->plugin_path() == broker_path) 287 return *iter; 288 } 289 290 return NULL; 291 } 292 293 PluginProcessHost* PluginServiceImpl::FindOrStartNpapiPluginProcess( 294 int render_process_id, 295 const base::FilePath& plugin_path) { 296 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 297 298 if (filter_ && !filter_->CanLoadPlugin(render_process_id, plugin_path)) 299 return NULL; 300 301 PluginProcessHost* plugin_host = FindNpapiPluginProcess(plugin_path); 302 if (plugin_host) 303 return plugin_host; 304 305 WebPluginInfo info; 306 if (!GetPluginInfoByPath(plugin_path, &info)) { 307 return NULL; 308 } 309 310 // Record when NPAPI Flash process is started for the first time. 311 static bool counted = false; 312 if (!counted && UTF16ToUTF8(info.name) == kFlashPluginName) { 313 counted = true; 314 UMA_HISTOGRAM_ENUMERATION("Plugin.FlashUsage", 315 START_NPAPI_FLASH_AT_LEAST_ONCE, 316 FLASH_USAGE_ENUM_COUNT); 317 } 318 319 // This plugin isn't loaded by any plugin process, so create a new process. 320 scoped_ptr<PluginProcessHost> new_host(new PluginProcessHost()); 321 if (!new_host->Init(info)) { 322 NOTREACHED(); // Init is not expected to fail. 323 return NULL; 324 } 325 return new_host.release(); 326 } 327 328 PpapiPluginProcessHost* PluginServiceImpl::FindOrStartPpapiPluginProcess( 329 int render_process_id, 330 const base::FilePath& plugin_path, 331 const base::FilePath& profile_data_directory, 332 PpapiPluginProcessHost::PluginClient* client) { 333 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 334 335 if (filter_ && !filter_->CanLoadPlugin(render_process_id, plugin_path)) 336 return NULL; 337 338 PpapiPluginProcessHost* plugin_host = 339 FindPpapiPluginProcess(plugin_path, profile_data_directory); 340 if (plugin_host) 341 return plugin_host; 342 343 // Validate that the plugin is actually registered. 344 PepperPluginInfo* info = GetRegisteredPpapiPluginInfo(plugin_path); 345 if (!info) 346 return NULL; 347 348 // Record when PPAPI Flash process is started for the first time. 349 static bool counted = false; 350 if (!counted && info->name == kFlashPluginName) { 351 counted = true; 352 UMA_HISTOGRAM_ENUMERATION("Plugin.FlashUsage", 353 START_PPAPI_FLASH_AT_LEAST_ONCE, 354 FLASH_USAGE_ENUM_COUNT); 355 } 356 357 // This plugin isn't loaded by any plugin process, so create a new process. 358 return PpapiPluginProcessHost::CreatePluginHost( 359 *info, profile_data_directory, 360 client->GetResourceContext()->GetHostResolver()); 361 } 362 363 PpapiPluginProcessHost* PluginServiceImpl::FindOrStartPpapiBrokerProcess( 364 int render_process_id, 365 const base::FilePath& plugin_path) { 366 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 367 368 if (filter_ && !filter_->CanLoadPlugin(render_process_id, plugin_path)) 369 return NULL; 370 371 PpapiPluginProcessHost* plugin_host = FindPpapiBrokerProcess(plugin_path); 372 if (plugin_host) 373 return plugin_host; 374 375 // Validate that the plugin is actually registered. 376 PepperPluginInfo* info = GetRegisteredPpapiPluginInfo(plugin_path); 377 if (!info) 378 return NULL; 379 380 // TODO(ddorwin): Uncomment once out of process is supported. 381 // DCHECK(info->is_out_of_process); 382 383 // This broker isn't loaded by any broker process, so create a new process. 384 return PpapiPluginProcessHost::CreateBrokerHost(*info); 385 } 386 387 void PluginServiceImpl::OpenChannelToNpapiPlugin( 388 int render_process_id, 389 int render_view_id, 390 const GURL& url, 391 const GURL& page_url, 392 const std::string& mime_type, 393 PluginProcessHost::Client* client) { 394 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 395 DCHECK(!ContainsKey(pending_plugin_clients_, client)); 396 pending_plugin_clients_.insert(client); 397 398 // Make sure plugins are loaded if necessary. 399 PluginServiceFilterParams params = { 400 render_process_id, 401 render_view_id, 402 page_url, 403 client->GetResourceContext() 404 }; 405 GetPlugins(base::Bind( 406 &PluginServiceImpl::ForwardGetAllowedPluginForOpenChannelToPlugin, 407 base::Unretained(this), params, url, mime_type, client)); 408 } 409 410 void PluginServiceImpl::OpenChannelToPpapiPlugin( 411 int render_process_id, 412 const base::FilePath& plugin_path, 413 const base::FilePath& profile_data_directory, 414 PpapiPluginProcessHost::PluginClient* client) { 415 PpapiPluginProcessHost* plugin_host = FindOrStartPpapiPluginProcess( 416 render_process_id, plugin_path, profile_data_directory, client); 417 if (plugin_host) { 418 plugin_host->OpenChannelToPlugin(client); 419 } else { 420 // Send error. 421 client->OnPpapiChannelOpened(IPC::ChannelHandle(), base::kNullProcessId, 0); 422 } 423 } 424 425 void PluginServiceImpl::OpenChannelToPpapiBroker( 426 int render_process_id, 427 const base::FilePath& path, 428 PpapiPluginProcessHost::BrokerClient* client) { 429 PpapiPluginProcessHost* plugin_host = FindOrStartPpapiBrokerProcess( 430 render_process_id, path); 431 if (plugin_host) { 432 plugin_host->OpenChannelToPlugin(client); 433 } else { 434 // Send error. 435 client->OnPpapiChannelOpened(IPC::ChannelHandle(), base::kNullProcessId, 0); 436 } 437 } 438 439 void PluginServiceImpl::CancelOpenChannelToNpapiPlugin( 440 PluginProcessHost::Client* client) { 441 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 442 DCHECK(ContainsKey(pending_plugin_clients_, client)); 443 pending_plugin_clients_.erase(client); 444 } 445 446 void PluginServiceImpl::ForwardGetAllowedPluginForOpenChannelToPlugin( 447 const PluginServiceFilterParams& params, 448 const GURL& url, 449 const std::string& mime_type, 450 PluginProcessHost::Client* client, 451 const std::vector<WebPluginInfo>&) { 452 GetAllowedPluginForOpenChannelToPlugin(params.render_process_id, 453 params.render_view_id, url, params.page_url, mime_type, client, 454 params.resource_context); 455 } 456 457 void PluginServiceImpl::GetAllowedPluginForOpenChannelToPlugin( 458 int render_process_id, 459 int render_view_id, 460 const GURL& url, 461 const GURL& page_url, 462 const std::string& mime_type, 463 PluginProcessHost::Client* client, 464 ResourceContext* resource_context) { 465 WebPluginInfo info; 466 bool allow_wildcard = true; 467 bool found = GetPluginInfo( 468 render_process_id, render_view_id, resource_context, 469 url, page_url, mime_type, allow_wildcard, 470 NULL, &info, NULL); 471 base::FilePath plugin_path; 472 if (found) 473 plugin_path = info.path; 474 475 // Now we jump back to the IO thread to finish opening the channel. 476 BrowserThread::PostTask( 477 BrowserThread::IO, FROM_HERE, 478 base::Bind(&PluginServiceImpl::FinishOpenChannelToPlugin, 479 base::Unretained(this), 480 render_process_id, 481 plugin_path, 482 client)); 483 } 484 485 void PluginServiceImpl::FinishOpenChannelToPlugin( 486 int render_process_id, 487 const base::FilePath& plugin_path, 488 PluginProcessHost::Client* client) { 489 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 490 491 // Make sure it hasn't been canceled yet. 492 if (!ContainsKey(pending_plugin_clients_, client)) 493 return; 494 pending_plugin_clients_.erase(client); 495 496 PluginProcessHost* plugin_host = FindOrStartNpapiPluginProcess( 497 render_process_id, plugin_path); 498 if (plugin_host) { 499 client->OnFoundPluginProcessHost(plugin_host); 500 plugin_host->OpenChannelToPlugin(client); 501 } else { 502 client->OnError(); 503 } 504 } 505 506 bool PluginServiceImpl::GetPluginInfoArray( 507 const GURL& url, 508 const std::string& mime_type, 509 bool allow_wildcard, 510 std::vector<WebPluginInfo>* plugins, 511 std::vector<std::string>* actual_mime_types) { 512 bool use_stale = false; 513 PluginList::Singleton()->GetPluginInfoArray( 514 url, mime_type, allow_wildcard, &use_stale, NPAPIPluginsSupported(), 515 plugins, actual_mime_types); 516 return use_stale; 517 } 518 519 bool PluginServiceImpl::GetPluginInfo(int render_process_id, 520 int render_view_id, 521 ResourceContext* context, 522 const GURL& url, 523 const GURL& page_url, 524 const std::string& mime_type, 525 bool allow_wildcard, 526 bool* is_stale, 527 WebPluginInfo* info, 528 std::string* actual_mime_type) { 529 std::vector<WebPluginInfo> plugins; 530 std::vector<std::string> mime_types; 531 bool stale = GetPluginInfoArray( 532 url, mime_type, allow_wildcard, &plugins, &mime_types); 533 if (is_stale) 534 *is_stale = stale; 535 536 for (size_t i = 0; i < plugins.size(); ++i) { 537 if (!filter_ || filter_->IsPluginAvailable(render_process_id, 538 render_view_id, 539 context, 540 url, 541 page_url, 542 &plugins[i])) { 543 *info = plugins[i]; 544 if (actual_mime_type) 545 *actual_mime_type = mime_types[i]; 546 return true; 547 } 548 } 549 return false; 550 } 551 552 bool PluginServiceImpl::GetPluginInfoByPath(const base::FilePath& plugin_path, 553 WebPluginInfo* info) { 554 std::vector<WebPluginInfo> plugins; 555 PluginList::Singleton()->GetPluginsNoRefresh(&plugins); 556 557 for (std::vector<WebPluginInfo>::iterator it = plugins.begin(); 558 it != plugins.end(); 559 ++it) { 560 if (it->path == plugin_path) { 561 *info = *it; 562 return true; 563 } 564 } 565 566 return false; 567 } 568 569 string16 PluginServiceImpl::GetPluginDisplayNameByPath( 570 const base::FilePath& path) { 571 string16 plugin_name = path.LossyDisplayName(); 572 WebPluginInfo info; 573 if (PluginService::GetInstance()->GetPluginInfoByPath(path, &info) && 574 !info.name.empty()) { 575 plugin_name = info.name; 576 #if defined(OS_MACOSX) 577 // Many plugins on the Mac have .plugin in the actual name, which looks 578 // terrible, so look for that and strip it off if present. 579 const std::string kPluginExtension = ".plugin"; 580 if (EndsWith(plugin_name, ASCIIToUTF16(kPluginExtension), true)) 581 plugin_name.erase(plugin_name.length() - kPluginExtension.length()); 582 #endif // OS_MACOSX 583 } 584 return plugin_name; 585 } 586 587 void PluginServiceImpl::GetPlugins(const GetPluginsCallback& callback) { 588 scoped_refptr<base::MessageLoopProxy> target_loop( 589 base::MessageLoop::current()->message_loop_proxy()); 590 591 if (LoadPluginListInProcess()) { 592 BrowserThread::GetBlockingPool()-> 593 PostSequencedWorkerTaskWithShutdownBehavior( 594 plugin_list_token_, 595 FROM_HERE, 596 base::Bind(&PluginServiceImpl::GetPluginsInternal, 597 base::Unretained(this), 598 target_loop, callback), 599 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN); 600 return; 601 } 602 #if defined(OS_POSIX) 603 std::vector<WebPluginInfo> cached_plugins; 604 if (PluginList::Singleton()->GetPluginsNoRefresh(&cached_plugins)) { 605 // Can't assume the caller is reentrant. 606 target_loop->PostTask(FROM_HERE, 607 base::Bind(callback, cached_plugins)); 608 } else { 609 // If we switch back to loading plugins in process, then we need to make 610 // sure g_thread_init() gets called since plugins may call glib at load. 611 if (!plugin_loader_.get()) 612 plugin_loader_ = new PluginLoaderPosix; 613 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, 614 base::Bind(&PluginLoaderPosix::LoadPlugins, plugin_loader_, 615 target_loop, callback)); 616 } 617 #else 618 NOTREACHED(); 619 #endif 620 } 621 622 void PluginServiceImpl::GetPluginsInternal( 623 base::MessageLoopProxy* target_loop, 624 const PluginService::GetPluginsCallback& callback) { 625 DCHECK(BrowserThread::GetBlockingPool()->IsRunningSequenceOnCurrentThread( 626 plugin_list_token_)); 627 628 std::vector<WebPluginInfo> plugins; 629 PluginList::Singleton()->GetPlugins(&plugins, NPAPIPluginsSupported()); 630 631 target_loop->PostTask(FROM_HERE, 632 base::Bind(callback, plugins)); 633 } 634 635 void PluginServiceImpl::OnWaitableEventSignaled( 636 base::WaitableEvent* waitable_event) { 637 #if defined(OS_WIN) 638 if (waitable_event == hkcu_event_) { 639 hkcu_key_.StartWatching(); 640 } else { 641 hklm_key_.StartWatching(); 642 } 643 644 PluginList::Singleton()->RefreshPlugins(); 645 PurgePluginListCache(NULL, false); 646 #else 647 // This event should only get signaled on a Windows machine. 648 NOTREACHED(); 649 #endif // defined(OS_WIN) 650 } 651 652 void PluginServiceImpl::RegisterPepperPlugins() { 653 ComputePepperPluginList(&ppapi_plugins_); 654 for (size_t i = 0; i < ppapi_plugins_.size(); ++i) { 655 RegisterInternalPlugin(ppapi_plugins_[i].ToWebPluginInfo(), true); 656 } 657 } 658 659 // There should generally be very few plugins so a brute-force search is fine. 660 PepperPluginInfo* PluginServiceImpl::GetRegisteredPpapiPluginInfo( 661 const base::FilePath& plugin_path) { 662 PepperPluginInfo* info = NULL; 663 for (size_t i = 0; i < ppapi_plugins_.size(); i++) { 664 if (ppapi_plugins_[i].path == plugin_path) { 665 info = &ppapi_plugins_[i]; 666 break; 667 } 668 } 669 if (info) 670 return info; 671 // We did not find the plugin in our list. But wait! the plugin can also 672 // be a latecomer, as it happens with pepper flash. This information 673 // can be obtained from the PluginList singleton and we can use it to 674 // construct it and add it to the list. This same deal needs to be done 675 // in the renderer side in PepperPluginRegistry. 676 WebPluginInfo webplugin_info; 677 if (!GetPluginInfoByPath(plugin_path, &webplugin_info)) 678 return NULL; 679 PepperPluginInfo new_pepper_info; 680 if (!MakePepperPluginInfo(webplugin_info, &new_pepper_info)) 681 return NULL; 682 ppapi_plugins_.push_back(new_pepper_info); 683 return &ppapi_plugins_[ppapi_plugins_.size() - 1]; 684 } 685 686 #if defined(OS_POSIX) && !defined(OS_OPENBSD) && !defined(OS_ANDROID) 687 // static 688 void PluginServiceImpl::RegisterFilePathWatcher(FilePathWatcher* watcher, 689 const base::FilePath& path) { 690 bool result = watcher->Watch(path, false, 691 base::Bind(&NotifyPluginDirChanged)); 692 DCHECK(result); 693 } 694 #endif 695 696 void PluginServiceImpl::SetFilter(PluginServiceFilter* filter) { 697 filter_ = filter; 698 } 699 700 PluginServiceFilter* PluginServiceImpl::GetFilter() { 701 return filter_; 702 } 703 704 void PluginServiceImpl::ForcePluginShutdown(const base::FilePath& plugin_path) { 705 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { 706 BrowserThread::PostTask( 707 BrowserThread::IO, FROM_HERE, 708 base::Bind(&PluginServiceImpl::ForcePluginShutdown, 709 base::Unretained(this), plugin_path)); 710 return; 711 } 712 713 PluginProcessHost* plugin = FindNpapiPluginProcess(plugin_path); 714 if (plugin) 715 plugin->ForceShutdown(); 716 } 717 718 static const unsigned int kMaxCrashesPerInterval = 3; 719 static const unsigned int kCrashesInterval = 120; 720 721 void PluginServiceImpl::RegisterPluginCrash(const base::FilePath& path) { 722 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 723 std::map<base::FilePath, std::vector<base::Time> >::iterator i = 724 crash_times_.find(path); 725 if (i == crash_times_.end()) { 726 crash_times_[path] = std::vector<base::Time>(); 727 i = crash_times_.find(path); 728 } 729 if (i->second.size() == kMaxCrashesPerInterval) { 730 i->second.erase(i->second.begin()); 731 } 732 base::Time time = base::Time::Now(); 733 i->second.push_back(time); 734 } 735 736 bool PluginServiceImpl::IsPluginUnstable(const base::FilePath& path) { 737 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 738 std::map<base::FilePath, std::vector<base::Time> >::const_iterator i = 739 crash_times_.find(path); 740 if (i == crash_times_.end()) { 741 return false; 742 } 743 if (i->second.size() != kMaxCrashesPerInterval) { 744 return false; 745 } 746 base::TimeDelta delta = base::Time::Now() - i->second[0]; 747 return delta.InSeconds() <= kCrashesInterval; 748 } 749 750 void PluginServiceImpl::RefreshPlugins() { 751 PluginList::Singleton()->RefreshPlugins(); 752 } 753 754 void PluginServiceImpl::AddExtraPluginPath(const base::FilePath& path) { 755 if (!NPAPIPluginsSupported()) { 756 // TODO(jam): remove and just have CHECK once we're sure this doesn't get 757 // triggered. 758 DLOG(INFO) << "NPAPI plugins not supported"; 759 return; 760 } 761 PluginList::Singleton()->AddExtraPluginPath(path); 762 } 763 764 void PluginServiceImpl::RemoveExtraPluginPath(const base::FilePath& path) { 765 PluginList::Singleton()->RemoveExtraPluginPath(path); 766 } 767 768 void PluginServiceImpl::AddExtraPluginDir(const base::FilePath& path) { 769 PluginList::Singleton()->AddExtraPluginDir(path); 770 } 771 772 void PluginServiceImpl::RegisterInternalPlugin( 773 const WebPluginInfo& info, 774 bool add_at_beginning) { 775 if (!NPAPIPluginsSupported() && 776 info.type == WebPluginInfo::PLUGIN_TYPE_NPAPI) { 777 DLOG(INFO) << "Don't register NPAPI plugins when they're not supported"; 778 return; 779 } 780 PluginList::Singleton()->RegisterInternalPlugin(info, add_at_beginning); 781 } 782 783 void PluginServiceImpl::UnregisterInternalPlugin(const base::FilePath& path) { 784 PluginList::Singleton()->UnregisterInternalPlugin(path); 785 } 786 787 void PluginServiceImpl::GetInternalPlugins( 788 std::vector<WebPluginInfo>* plugins) { 789 PluginList::Singleton()->GetInternalPlugins(plugins); 790 } 791 792 bool PluginServiceImpl::NPAPIPluginsSupported() { 793 #if defined(OS_WIN) || defined(OS_MACOSX) || (defined(OS_LINUX) && !defined(USE_AURA)) 794 return true; 795 #else 796 return false; 797 #endif 798 } 799 800 void PluginServiceImpl::DisablePluginsDiscoveryForTesting() { 801 PluginList::Singleton()->DisablePluginsDiscovery(); 802 } 803 804 #if defined(OS_MACOSX) 805 void PluginServiceImpl::AppActivated() { 806 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, 807 base::Bind(&NotifyPluginsOfActivation)); 808 } 809 #elif defined(OS_WIN) 810 811 bool GetPluginPropertyFromWindow( 812 HWND window, const wchar_t* plugin_atom_property, 813 base::string16* plugin_property) { 814 ATOM plugin_atom = reinterpret_cast<ATOM>( 815 GetPropW(window, plugin_atom_property)); 816 if (plugin_atom != 0) { 817 WCHAR plugin_property_local[MAX_PATH] = {0}; 818 GlobalGetAtomNameW(plugin_atom, 819 plugin_property_local, 820 ARRAYSIZE(plugin_property_local)); 821 *plugin_property = plugin_property_local; 822 return true; 823 } 824 return false; 825 } 826 827 bool PluginServiceImpl::GetPluginInfoFromWindow( 828 HWND window, 829 base::string16* plugin_name, 830 base::string16* plugin_version) { 831 if (!IsPluginWindow(window)) 832 return false; 833 834 GetPluginPropertyFromWindow( 835 window, kPluginNameAtomProperty, plugin_name); 836 GetPluginPropertyFromWindow( 837 window, kPluginVersionAtomProperty, plugin_version); 838 return true; 839 } 840 841 bool PluginServiceImpl::IsPluginWindow(HWND window) { 842 return ui::GetClassName(window) == base::string16(kNativeWindowClassName); 843 } 844 #endif 845 846 } // namespace content 847