Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/win/enumerate_modules_model.h" | 5 #include "chrome/browser/win/enumerate_modules_model.h" |
| 6 | 6 |
| 7 #include <softpub.h> | 7 #include <softpub.h> |
| 8 #include <stddef.h> | 8 #include <stddef.h> |
| 9 #include <stdint.h> | 9 #include <stdint.h> |
| 10 #include <tlhelp32.h> | 10 #include <tlhelp32.h> |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 53 return a.status > b.status; | 53 return a.status > b.status; |
| 54 | 54 |
| 55 if (a.location == b.location) | 55 if (a.location == b.location) |
| 56 return a.name < b.name; | 56 return a.name < b.name; |
| 57 | 57 |
| 58 return a.location < b.location; | 58 return a.location < b.location; |
| 59 } | 59 } |
| 60 | 60 |
| 61 namespace { | 61 namespace { |
| 62 | 62 |
| 63 // The default amount of time between module inspections. This prevents | |
| 64 // certificate inspection and validation from using a large amount of CPU and | |
| 65 // battery immediately after startup. | |
| 66 const base::TimeDelta kDefaultPerModuleDelay = base::TimeDelta::FromSeconds(1); | |
|
Patrick Monette
2016/10/14 22:45:55
constexpr
chrisha
2016/10/17 15:32:21
Done.
| |
| 67 | |
| 63 // A struct to help de-duping modules before adding them to the enumerated | 68 // A struct to help de-duping modules before adding them to the enumerated |
| 64 // modules vector. | 69 // modules vector. |
| 65 struct FindModule { | 70 struct FindModule { |
| 66 public: | 71 public: |
| 67 explicit FindModule(const ModuleEnumerator::Module& x) | 72 explicit FindModule(const ModuleEnumerator::Module& x) |
| 68 : module(x) {} | 73 : module(x) {} |
| 69 bool operator()(const ModuleEnumerator::Module& module_in) const { | 74 bool operator()(const ModuleEnumerator::Module& module_in) const { |
| 70 return (module.location == module_in.location) && | 75 return (module.location == module_in.location) && |
| 71 (module.name == module_in.name); | 76 (module.name == module_in.name); |
| 72 } | 77 } |
| (...skipping 313 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 386 // to them. Remove that. | 391 // to them. Remove that. |
| 387 size_t first_space = module->version.find_first_of(L" "); | 392 size_t first_space = module->version.find_first_of(L" "); |
| 388 if (first_space != base::string16::npos) | 393 if (first_space != base::string16::npos) |
| 389 module->version = module->version.substr(0, first_space); | 394 module->version = module->version.substr(0, first_space); |
| 390 | 395 |
| 391 module->normalized = true; | 396 module->normalized = true; |
| 392 } | 397 } |
| 393 | 398 |
| 394 ModuleEnumerator::ModuleEnumerator(EnumerateModulesModel* observer) | 399 ModuleEnumerator::ModuleEnumerator(EnumerateModulesModel* observer) |
| 395 : enumerated_modules_(nullptr), | 400 : enumerated_modules_(nullptr), |
| 396 observer_(observer) { | 401 observer_(observer), |
| 402 per_module_delay_(kDefaultPerModuleDelay) { | |
| 397 } | 403 } |
| 398 | 404 |
| 399 ModuleEnumerator::~ModuleEnumerator() { | 405 ModuleEnumerator::~ModuleEnumerator() { |
| 400 } | 406 } |
| 401 | 407 |
| 402 void ModuleEnumerator::ScanNow(ModulesVector* list) { | 408 void ModuleEnumerator::ScanNow(ModulesVector* list) { |
| 403 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 409 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 404 enumerated_modules_ = list; | 410 enumerated_modules_ = list; |
| 405 | 411 |
| 406 // This object can't be reaped until it has finished scanning, so its safe | 412 // This object can't be reaped until it has finished scanning, so its safe |
| 407 // to post a raw pointer to another thread. It will simply be leaked if the | 413 // to post a raw pointer to another thread. It will simply be leaked if the |
| 408 // scanning has not been finished before shutdown. | 414 // scanning has not been finished before shutdown. |
| 409 BrowserThread::GetBlockingPool()->PostWorkerTaskWithShutdownBehavior( | 415 BrowserThread::GetBlockingPool()->PostWorkerTaskWithShutdownBehavior( |
| 410 FROM_HERE, | 416 FROM_HERE, |
| 411 base::Bind(&ModuleEnumerator::ScanImpl, | 417 base::Bind(&ModuleEnumerator::ScanImplStart, |
| 412 base::Unretained(this)), | 418 base::Unretained(this)), |
| 413 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN); | 419 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN); |
| 414 } | 420 } |
| 415 | 421 |
| 416 void ModuleEnumerator::ScanImpl() { | 422 void ModuleEnumerator::SetPerModuleDelayToZero() { |
| 423 // Set the delay to zero so the modules enumerate as quickly as possible. | |
| 424 per_module_delay_ = base::TimeDelta::FromSeconds(0); | |
| 425 } | |
| 426 | |
| 427 void ModuleEnumerator::ScanImplStart() { | |
| 417 base::TimeTicks start_time = base::TimeTicks::Now(); | 428 base::TimeTicks start_time = base::TimeTicks::Now(); |
| 418 | 429 |
| 419 // The provided destination for the enumerated modules should be empty, as it | 430 // The provided destination for the enumerated modules should be empty, as it |
| 420 // should only be populated once, by a single ModuleEnumerator instance. See | 431 // should only be populated once, by a single ModuleEnumerator instance. See |
| 421 // EnumerateModulesModel::ScanNow for details. | 432 // EnumerateModulesModel::ScanNow for details. |
| 422 DCHECK(enumerated_modules_->empty()); | 433 DCHECK(enumerated_modules_->empty()); |
| 423 | 434 |
| 424 // Make sure the path mapping vector is setup so we can collapse paths. | 435 // Make sure the path mapping vector is setup so we can collapse paths. |
| 425 PreparePathMappings(); | 436 PreparePathMappings(); |
| 426 | 437 |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 437 checkpoint2 = base::TimeTicks::Now(); | 448 checkpoint2 = base::TimeTicks::Now(); |
| 438 UMA_HISTOGRAM_TIMES("Conflicts.EnumerateShellExtensions", | 449 UMA_HISTOGRAM_TIMES("Conflicts.EnumerateShellExtensions", |
| 439 checkpoint2 - checkpoint); | 450 checkpoint2 - checkpoint); |
| 440 | 451 |
| 441 checkpoint = checkpoint2; | 452 checkpoint = checkpoint2; |
| 442 EnumerateWinsockModules(); | 453 EnumerateWinsockModules(); |
| 443 checkpoint2 = base::TimeTicks::Now(); | 454 checkpoint2 = base::TimeTicks::Now(); |
| 444 UMA_HISTOGRAM_TIMES("Conflicts.EnumerateWinsockModules", | 455 UMA_HISTOGRAM_TIMES("Conflicts.EnumerateWinsockModules", |
| 445 checkpoint2 - checkpoint); | 456 checkpoint2 - checkpoint); |
| 446 | 457 |
| 458 enumeration_total_time_ = base::TimeTicks::Now() - start_time; | |
| 459 | |
| 460 // Post a delayed task to scan the first module. This forwards directly to | |
| 461 // ScanImplFinish if there are no modules to scan. | |
| 462 BrowserThread::GetBlockingPool()->PostDelayedWorkerTask( | |
| 463 FROM_HERE, | |
| 464 base::Bind(&ModuleEnumerator::ScanImplModule, | |
| 465 base::Unretained(this), | |
| 466 0), | |
| 467 per_module_delay_); | |
| 468 } | |
| 469 | |
| 470 void ModuleEnumerator::ScanImplDelay(size_t index) { | |
| 471 // Bounce this over to a CONTINUE_ON_SHUTDOWN task in the same pool. This is | |
| 472 // necessary to prevent shutdown hangs while inspecting a module. | |
| 473 BrowserThread::GetBlockingPool()->PostWorkerTaskWithShutdownBehavior( | |
| 474 FROM_HERE, | |
| 475 base::Bind(&ModuleEnumerator::ScanImplModule, | |
| 476 base::Unretained(this), | |
| 477 index), | |
| 478 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN); | |
| 479 } | |
| 480 | |
| 481 void ModuleEnumerator::ScanImplModule(size_t index) { | |
| 482 while (index < enumerated_modules_->size()) { | |
| 483 base::TimeTicks start_time = base::TimeTicks::Now(); | |
| 484 Module& entry = enumerated_modules_->at(index); | |
| 485 PopulateModuleInformation(&entry); | |
| 486 NormalizeModule(&entry); | |
| 487 CollapsePath(&entry); | |
| 488 base::TimeDelta elapsed = base::TimeTicks::Now() - start_time; | |
| 489 enumeration_inspection_time_ += elapsed; | |
| 490 enumeration_total_time_ += elapsed; | |
| 491 | |
| 492 // If the delay has been set to zero then simply finish the rest of the | |
| 493 // enumeration in this already started task. | |
| 494 if (per_module_delay_.is_zero()) { | |
|
Patrick Monette
2016/10/14 22:45:55
Reverse the conditional for simpler code? (Let you
chrisha
2016/10/17 15:32:21
Done.
| |
| 495 ++index; | |
| 496 continue; | |
| 497 } else { | |
| 498 // Bounce back over to ScanImplDelay, which will bounce back to this | |
| 499 // function and inspect the next module. | |
| 500 BrowserThread::GetBlockingPool()->PostDelayedWorkerTask( | |
| 501 FROM_HERE, | |
| 502 base::Bind(&ModuleEnumerator::ScanImplDelay, | |
| 503 base::Unretained(this), | |
| 504 index + 1), | |
| 505 per_module_delay_); | |
| 506 return; | |
| 507 } | |
| 508 } | |
| 509 | |
| 510 // Getting here means that all of the modules have been inspected. | |
| 511 return ScanImplFinish(); | |
| 512 } | |
| 513 | |
| 514 void ModuleEnumerator::ScanImplFinish() { | |
| 447 // TODO(chrisha): Annotate any modules that are suspicious/bad. | 515 // TODO(chrisha): Annotate any modules that are suspicious/bad. |
| 448 | 516 |
| 449 ReportThirdPartyMetrics(); | 517 ReportThirdPartyMetrics(); |
| 450 | 518 |
| 451 std::sort(enumerated_modules_->begin(), | 519 std::sort(enumerated_modules_->begin(), |
| 452 enumerated_modules_->end(), ModuleSort); | 520 enumerated_modules_->end(), ModuleSort); |
| 453 | 521 |
| 522 UMA_HISTOGRAM_TIMES("Conflicts.EnumerationInspectionTime", | |
| 523 enumeration_inspection_time_); | |
| 454 UMA_HISTOGRAM_TIMES("Conflicts.EnumerationTotalTime", | 524 UMA_HISTOGRAM_TIMES("Conflicts.EnumerationTotalTime", |
| 455 base::TimeTicks::Now() - start_time); | 525 enumeration_total_time_); |
| 456 | 526 |
| 457 // Send a reply back on the UI thread. The |observer_| outlives this | 527 // Send a reply back on the UI thread. The |observer_| outlives this |
| 458 // enumerator, so posting a raw pointer is safe. This is done last as | 528 // enumerator, so posting a raw pointer is safe. This is done last as |
| 459 // DoneScanning will then reap this ModuleEnumerator. | 529 // DoneScanning will then reap this ModuleEnumerator. |
| 460 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | 530 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
| 461 base::Bind(&EnumerateModulesModel::DoneScanning, | 531 base::Bind(&EnumerateModulesModel::DoneScanning, |
| 462 base::Unretained(observer_))); | 532 base::Unretained(observer_))); |
| 463 } | 533 } |
| 464 | 534 |
| 465 void ModuleEnumerator::EnumerateLoadedModules() { | 535 void ModuleEnumerator::EnumerateLoadedModules() { |
| 466 // Get all modules in the current process. | 536 // Get all modules in the current process. |
| 467 base::win::ScopedHandle snap(::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, | 537 base::win::ScopedHandle snap(::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, |
| 468 ::GetCurrentProcessId())); | 538 ::GetCurrentProcessId())); |
| 469 if (!snap.Get()) | 539 if (!snap.Get()) |
| 470 return; | 540 return; |
| 471 | 541 |
| 472 // Walk the module list. | 542 // Walk the module list. |
| 473 MODULEENTRY32 module = { sizeof(module) }; | 543 MODULEENTRY32 module = { sizeof(module) }; |
| 474 if (!::Module32First(snap.Get(), &module)) | 544 if (!::Module32First(snap.Get(), &module)) |
| 475 return; | 545 return; |
| 476 | 546 |
| 477 do { | 547 do { |
| 478 // It would be weird to present chrome.exe as a loaded module. | 548 // It would be weird to present chrome.exe as a loaded module. |
| 479 if (_wcsicmp(chrome::kBrowserProcessExecutableName, module.szModule) == 0) | 549 if (_wcsicmp(chrome::kBrowserProcessExecutableName, module.szModule) == 0) |
| 480 continue; | 550 continue; |
| 481 | 551 |
| 482 Module entry; | 552 Module entry; |
| 483 entry.type = LOADED_MODULE; | 553 entry.type = LOADED_MODULE; |
| 484 entry.location = module.szExePath; | 554 entry.location = module.szExePath; |
| 485 PopulateModuleInformation(&entry); | |
| 486 | |
| 487 NormalizeModule(&entry); | |
| 488 CollapsePath(&entry); | |
| 489 enumerated_modules_->push_back(entry); | 555 enumerated_modules_->push_back(entry); |
| 490 } while (::Module32Next(snap.Get(), &module)); | 556 } while (::Module32Next(snap.Get(), &module)); |
| 491 } | 557 } |
| 492 | 558 |
| 493 void ModuleEnumerator::EnumerateShellExtensions() { | 559 void ModuleEnumerator::EnumerateShellExtensions() { |
| 494 ReadShellExtensions(HKEY_LOCAL_MACHINE); | 560 ReadShellExtensions(HKEY_LOCAL_MACHINE); |
| 495 ReadShellExtensions(HKEY_CURRENT_USER); | 561 ReadShellExtensions(HKEY_CURRENT_USER); |
| 496 } | 562 } |
| 497 | 563 |
| 498 void ModuleEnumerator::ReadShellExtensions(HKEY parent) { | 564 void ModuleEnumerator::ReadShellExtensions(HKEY parent) { |
| 499 base::win::RegistryValueIterator registration(parent, kRegPath); | 565 base::win::RegistryValueIterator registration(parent, kRegPath); |
| 500 while (registration.Valid()) { | 566 while (registration.Valid()) { |
| 501 std::wstring key(std::wstring(L"CLSID\\") + registration.Name() + | 567 std::wstring key(std::wstring(L"CLSID\\") + registration.Name() + |
| 502 L"\\InProcServer32"); | 568 L"\\InProcServer32"); |
| 503 base::win::RegKey clsid; | 569 base::win::RegKey clsid; |
| 504 if (clsid.Open(HKEY_CLASSES_ROOT, key.c_str(), KEY_READ) != ERROR_SUCCESS) { | 570 if (clsid.Open(HKEY_CLASSES_ROOT, key.c_str(), KEY_READ) != ERROR_SUCCESS) { |
| 505 ++registration; | 571 ++registration; |
| 506 continue; | 572 continue; |
| 507 } | 573 } |
| 508 base::string16 dll; | 574 base::string16 dll; |
| 509 if (clsid.ReadValue(L"", &dll) != ERROR_SUCCESS) { | 575 if (clsid.ReadValue(L"", &dll) != ERROR_SUCCESS) { |
| 510 ++registration; | 576 ++registration; |
| 511 continue; | 577 continue; |
| 512 } | 578 } |
| 513 clsid.Close(); | 579 clsid.Close(); |
| 514 | 580 |
| 515 Module entry; | 581 Module entry; |
| 516 entry.type = SHELL_EXTENSION; | 582 entry.type = SHELL_EXTENSION; |
| 517 entry.location = dll; | 583 entry.location = dll; |
| 518 PopulateModuleInformation(&entry); | |
| 519 | |
| 520 NormalizeModule(&entry); | |
| 521 CollapsePath(&entry); | |
| 522 AddToListWithoutDuplicating(entry); | 584 AddToListWithoutDuplicating(entry); |
| 523 | 585 |
| 524 ++registration; | 586 ++registration; |
| 525 } | 587 } |
| 526 } | 588 } |
| 527 | 589 |
| 528 void ModuleEnumerator::EnumerateWinsockModules() { | 590 void ModuleEnumerator::EnumerateWinsockModules() { |
| 529 // Add to this list the Winsock LSP DLLs. | 591 // Add to this list the Winsock LSP DLLs. |
| 530 WinsockLayeredServiceProviderList layered_providers; | 592 WinsockLayeredServiceProviderList layered_providers; |
| 531 GetWinsockLayeredServiceProviders(&layered_providers); | 593 GetWinsockLayeredServiceProviders(&layered_providers); |
| 532 for (size_t i = 0; i < layered_providers.size(); ++i) { | 594 for (size_t i = 0; i < layered_providers.size(); ++i) { |
| 533 Module entry; | 595 Module entry; |
| 534 entry.type = WINSOCK_MODULE_REGISTRATION; | 596 entry.type = WINSOCK_MODULE_REGISTRATION; |
| 535 entry.status = NOT_MATCHED; | 597 entry.status = NOT_MATCHED; |
| 536 entry.normalized = false; | 598 entry.normalized = false; |
| 537 entry.location = layered_providers[i].path; | 599 entry.location = layered_providers[i].path; |
| 538 entry.description = layered_providers[i].name; | 600 entry.description = layered_providers[i].name; |
| 539 entry.recommended_action = NONE; | 601 entry.recommended_action = NONE; |
| 540 entry.duplicate_count = 0; | 602 entry.duplicate_count = 0; |
| 541 | 603 |
| 542 wchar_t expanded[MAX_PATH]; | 604 wchar_t expanded[MAX_PATH]; |
| 543 DWORD size = ExpandEnvironmentStrings( | 605 DWORD size = ExpandEnvironmentStrings( |
| 544 entry.location.c_str(), expanded, MAX_PATH); | 606 entry.location.c_str(), expanded, MAX_PATH); |
| 545 if (size != 0 && size <= MAX_PATH) { | 607 if (size != 0 && size <= MAX_PATH) |
| 546 GetCertificateInfo(base::FilePath(expanded), &entry.cert_info); | 608 entry.location = expanded; |
| 547 } | |
| 548 entry.version = base::IntToString16(layered_providers[i].version); | 609 entry.version = base::IntToString16(layered_providers[i].version); |
| 549 | |
| 550 // Paths have already been collapsed. | |
| 551 NormalizeModule(&entry); | |
| 552 AddToListWithoutDuplicating(entry); | 610 AddToListWithoutDuplicating(entry); |
| 553 } | 611 } |
| 554 } | 612 } |
| 555 | 613 |
| 556 void ModuleEnumerator::PopulateModuleInformation(Module* module) { | 614 void ModuleEnumerator::PopulateModuleInformation(Module* module) { |
| 557 module->status = NOT_MATCHED; | 615 module->status = NOT_MATCHED; |
| 558 module->duplicate_count = 0; | 616 module->duplicate_count = 0; |
| 559 module->normalized = false; | 617 module->normalized = false; |
| 560 GetCertificateInfo(base::FilePath(module->location), &module->cert_info); | 618 GetCertificateInfo(base::FilePath(module->location), &module->cert_info); |
| 561 module->recommended_action = NONE; | 619 module->recommended_action = NONE; |
| (...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 749 return modules_to_notify_about_; | 807 return modules_to_notify_about_; |
| 750 } | 808 } |
| 751 | 809 |
| 752 void EnumerateModulesModel::MaybePostScanningTask() { | 810 void EnumerateModulesModel::MaybePostScanningTask() { |
| 753 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 811 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 754 static bool done = false; | 812 static bool done = false; |
| 755 if (!done) { | 813 if (!done) { |
| 756 BrowserThread::PostAfterStartupTask( | 814 BrowserThread::PostAfterStartupTask( |
| 757 FROM_HERE, | 815 FROM_HERE, |
| 758 BrowserThread::GetTaskRunnerForThread(BrowserThread::UI), | 816 BrowserThread::GetTaskRunnerForThread(BrowserThread::UI), |
| 759 base::Bind(&EnumerateModulesModel::ScanNow, base::Unretained(this))); | 817 base::Bind(&EnumerateModulesModel::ScanNow, |
| 818 base::Unretained(this), | |
| 819 true)); | |
| 760 done = true; | 820 done = true; |
| 761 } | 821 } |
| 762 } | 822 } |
| 763 | 823 |
| 764 void EnumerateModulesModel::ScanNow() { | 824 void EnumerateModulesModel::ScanNow(bool background_mode) { |
| 765 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 825 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 766 | 826 |
| 767 // |module_enumerator_| is used as a lock to know whether or not there are | 827 // |module_enumerator_| is used as a lock to know whether or not there are |
| 768 // active/pending blocking pool tasks. If a module enumerator exists then a | 828 // active/pending blocking pool tasks. If a module enumerator exists then a |
| 769 // scan is already underway. Otherwise, either no scan has been completed or | 829 // scan is already underway. Otherwise, either no scan has been completed or |
| 770 // a scan has terminated. | 830 // a scan has terminated. |
| 771 if (module_enumerator_) | 831 if (module_enumerator_) { |
| 832 // If a scan is in progress and this request is for immediate results, then | |
| 833 // inform the background scan. This is done without any locks because the | |
| 834 // other thread only reads from the value that is being modified, and on | |
| 835 // Windows its an atomic write. | |
|
Patrick Monette
2016/10/14 22:45:55
Is this really safe?
chrisha
2016/10/17 15:32:21
Yeah, this is a standard trick. The variable is be
Patrick Monette
2016/10/17 19:52:40
Acknowledged.
| |
| 836 if (!background_mode) | |
| 837 module_enumerator_->SetPerModuleDelayToZero(); | |
| 772 return; | 838 return; |
| 839 } | |
| 773 | 840 |
| 774 // Only allow a single scan per process lifetime. Immediately notify any | 841 // Only allow a single scan per process lifetime. Immediately notify any |
| 775 // observers that the scan is complete. At this point |enumerated_modules_| is | 842 // observers that the scan is complete. At this point |enumerated_modules_| is |
| 776 // safe to access as no potentially racing blocking pool task can exist. | 843 // safe to access as no potentially racing blocking pool task can exist. |
| 777 if (!enumerated_modules_.empty()) { | 844 if (!enumerated_modules_.empty()) { |
| 778 FOR_EACH_OBSERVER(Observer, observers_, OnScanCompleted()); | 845 FOR_EACH_OBSERVER(Observer, observers_, OnScanCompleted()); |
| 779 return; | 846 return; |
| 780 } | 847 } |
| 781 | 848 |
| 782 // ScanNow does not block, rather it simply schedules a task. | 849 // ScanNow does not block, rather it simply schedules a task. |
| 783 module_enumerator_.reset(new ModuleEnumerator(this)); | 850 module_enumerator_.reset(new ModuleEnumerator(this)); |
| 851 if (!background_mode) | |
| 852 module_enumerator_->SetPerModuleDelayToZero(); | |
| 784 module_enumerator_->ScanNow(&enumerated_modules_); | 853 module_enumerator_->ScanNow(&enumerated_modules_); |
| 785 } | 854 } |
| 786 | 855 |
| 787 base::ListValue* EnumerateModulesModel::GetModuleList() { | 856 base::ListValue* EnumerateModulesModel::GetModuleList() { |
| 788 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 857 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 789 | 858 |
| 790 // If a |module_enumerator_| is still around then scanning has not yet | 859 // If a |module_enumerator_| is still around then scanning has not yet |
| 791 // completed, and it is unsafe to read from |enumerated_modules_|. | 860 // completed, and it is unsafe to read from |enumerated_modules_|. |
| 792 if (module_enumerator_.get()) | 861 if (module_enumerator_.get()) |
| 793 return nullptr; | 862 return nullptr; |
| (...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 912 module_enumerator_.reset(); | 981 module_enumerator_.reset(); |
| 913 | 982 |
| 914 UMA_HISTOGRAM_COUNTS_100("Conflicts.SuspectedBadModules", | 983 UMA_HISTOGRAM_COUNTS_100("Conflicts.SuspectedBadModules", |
| 915 suspected_bad_modules_detected_); | 984 suspected_bad_modules_detected_); |
| 916 UMA_HISTOGRAM_COUNTS_100("Conflicts.ConfirmedBadModules", | 985 UMA_HISTOGRAM_COUNTS_100("Conflicts.ConfirmedBadModules", |
| 917 confirmed_bad_modules_detected_); | 986 confirmed_bad_modules_detected_); |
| 918 | 987 |
| 919 // Forward the callback to any registered observers. | 988 // Forward the callback to any registered observers. |
| 920 FOR_EACH_OBSERVER(Observer, observers_, OnScanCompleted()); | 989 FOR_EACH_OBSERVER(Observer, observers_, OnScanCompleted()); |
| 921 } | 990 } |
| OLD | NEW |