Index: chrome/browser/enumerate_modules_model_win.cc |
=================================================================== |
--- chrome/browser/enumerate_modules_model_win.cc (revision 0) |
+++ chrome/browser/enumerate_modules_model_win.cc (revision 0) |
@@ -0,0 +1,626 @@ |
+// Copyright (c) 2010 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "chrome/browser/enumerate_modules_model_win.h" |
+ |
+#include <Tlhelp32.h> |
+#include <wintrust.h> |
+ |
+#include "app/l10n_util.h" |
+#include "app/win_util.h" |
+#include "base/command_line.h" |
+#include "base/environment.h" |
+#include "base/file_path.h" |
+#include "base/file_version_info_win.h" |
+#include "base/scoped_handle.h" |
+#include "base/sha2.h" |
+#include "base/string_number_conversions.h" |
+#include "base/string_util.h" |
+#include "base/utf_string_conversions.h" |
+#include "base/values.h" |
+#include "base/version.h" |
+#include "chrome/browser/net/service_providers_win.h" |
+#include "chrome/common/chrome_constants.h" |
+#include "chrome/common/chrome_switches.h" |
+#include "chrome/common/notification_service.h" |
+#include "grit/generated_resources.h" |
+ |
+// The period of time (in milliseconds) to wait until checking to see if any |
+// incompatible modules exist. |
+static const int kModuleCheckDelayMs = 60 * 1000; |
+ |
+// A sort method that sorts by ModuleType ordinal (loaded module at the top), |
+// then by full name (including path). |
+static bool ModuleSort(const ModuleEnumerator::Module& a, |
+ const ModuleEnumerator::Module& b) { |
+ if (a.type != b.type) |
+ return a.type < b.type; |
+ if (a.location == b.location) |
+ return a.name < b.name; |
+ |
+ return a.location < b.location; |
+} |
+ |
+namespace { |
+ |
+// Used to protect the LoadedModuleVector which is accessed |
+// from both the UI thread and the FILE thread. |
+Lock* lock = NULL; |
+ |
+} |
+ |
+// The browser process module blacklist. This lists all modules that are known |
+// to cause compatibility issues within the browser process. When adding to this |
+// list, make sure that all paths are lower-case, in long pathname form, end |
+// with a slash and use environments variables (or just look at one of the |
+// comments below and keep it consistent with that). When adding an entry with |
+// an environment variable not currently used in the list below, make sure to |
+// update the list in PreparePathMappings. Filename, Description/Signer, and |
+// Location must be entered as hashes (see GenerateHash). Filename is mandatory. |
+// Entries without any Description, Signer info, or Location will never be |
+// marked as confirmed bad (only as suspicious). |
+const ModuleEnumerator::BlacklistEntry ModuleEnumerator::kModuleBlacklist[] = { |
+ // Test DLLs, to demonstrate the feature. Will be removed soon. |
+ { // apphelp.dll, "%systemroot%\\system32\\" |
+ "f5fda581", "23d01d5b", "", "", "", NONE |
+ }, { // rsaenh.dll, "%systemroot%\\system32\\", "Microsoft Windows" |
+ "6af212cb", "23d01d5b", "7b47bf79", "", "", |
+ static_cast<RecommendedAction>(UPDATE | DISABLE | SEE_LINK) |
+ }, |
+ |
+ // NOTE: Please keep this list sorted by dll name, then location. |
+ |
+ // foldersizecolumn.dll. |
+ {"5ec91bd7", "", "", "", "", NONE}, |
+ |
+ // idmmbc.dll, "%programfiles%\\internet download manager\\", "Tonec Inc.". |
+ // See: http://crbug.com/26892/. |
+ {"b8dce5c3", "94541bf5", "d33ad640", "", "", NONE}, |
+ |
+ // imon.dll. See: http://crbug.com/21715. |
+ {"8f42f22e", "", "", "", "", NONE}, |
+ |
+ // is3lsp.dll. See: http://crbug.com/26892. |
+ {"7ffbdce9", "", "", "", "", NONE}, |
+ |
+ // nvlsp.dll. See: http://crbug.com/22083. |
+ {"37f907e2", "", "", "", "", NONE}, |
+ |
+ // nvshell.dll. See: http://crbug.com/3269. |
+ {"9290318f", "", "", "", "", NONE}, |
+ |
+ // securenet.dll. See: http://crbug.com/5165. |
+ {"9b266e1c", "", "", "", "", NONE}, |
+ |
+ // sgprxy.dll. |
+ {"005965ea", "", "", "", "", NONE}, |
+ |
+ // vaproxyd.dll. See: http://crbug.com/42445. |
+ {"0a1c7f81", "", "", "", "", NONE}, |
+ |
+ // vlsp.dll. See: http://crbug.com/22826. |
+ {"2e4eb93d", "", "", "", "", NONE}, |
+}; |
+ |
+// Generates an 8 digit hash from the input given. |
+static void GenerateHash(const std::string& input, std::string* output) { |
+ if (input.empty()) { |
+ *output = ""; |
+ return; |
+ } |
+ |
+ uint8 hash[4]; |
+ base::SHA256HashString(input, hash, sizeof(hash)); |
+ *output = StringToLowerASCII(base::HexEncode(hash, sizeof(hash))); |
+} |
+ |
+// ----------------------------------------------------------------------------- |
+ |
+// static |
+void ModuleEnumerator::NormalizeModule(Module* module) { |
+ string16 path = module->location; |
+ if (!win_util::ConvertToLongPath(path, &module->location)) |
+ module->location = path; |
+ |
+ module->location = l10n_util::ToLower(module->location); |
+ |
+ // Location contains the filename, so the last slash is where the path |
+ // ends. |
+ size_t last_slash = module->location.find_last_of(L"\\"); |
+ if (last_slash != string16::npos) { |
+ module->name = module->location.substr(last_slash + 1); |
+ module->location = module->location.substr(0, last_slash + 1); |
+ } else { |
+ module->name = module->location; |
+ module->location.clear(); |
+ } |
+ |
+ // Some version strings have things like (win7_rtm.090713-1255) appended |
+ // to them. Remove that. |
+ size_t first_space = module->version.find_first_of(L" "); |
+ if (first_space != string16::npos) |
+ module->version = module->version.substr(0, first_space); |
+ |
+ module->normalized = true; |
+} |
+ |
+// static |
+ModuleEnumerator::ModuleStatus ModuleEnumerator::Match( |
+ const ModuleEnumerator::Module& module, |
+ const ModuleEnumerator::BlacklistEntry& blacklisted) { |
+ // All modules must be normalized before matching against blacklist. |
+ DCHECK(module.normalized); |
+ // Filename is mandatory and version should not contain spaces. |
+ DCHECK(strlen(blacklisted.filename) > 0); |
+ DCHECK(!strstr(blacklisted.version_from, " ")); |
+ DCHECK(!strstr(blacklisted.version_to, " ")); |
+ |
+ std::string filename_hash, location_hash; |
+ GenerateHash(WideToUTF8(module.name), &filename_hash); |
+ GenerateHash(WideToUTF8(module.location), &location_hash); |
+ |
+ // Filenames are mandatory. Location is mandatory if given. |
+ if (filename_hash == blacklisted.filename && |
+ (std::string(blacklisted.location).empty() || |
+ location_hash == blacklisted.location)) { |
+ // We have a name match against the blacklist (and possibly location match |
+ // also), so check version. |
+ scoped_ptr<Version> module_version( |
+ Version::GetVersionFromString(module.version)); |
+ scoped_ptr<Version> version_min( |
+ Version::GetVersionFromString(blacklisted.version_from)); |
+ scoped_ptr<Version> version_max( |
+ Version::GetVersionFromString(blacklisted.version_to)); |
+ bool version_ok = !version_min.get() && !version_max.get(); |
+ if (!version_ok) { |
+ bool too_low = version_min.get() && |
+ (!module_version.get() || |
+ module_version->CompareTo(*version_min.get()) < 0); |
+ bool too_high = version_max.get() && |
+ (!module_version.get() || |
+ module_version->CompareTo(*version_max.get()) > 0); |
+ version_ok = !too_low && !too_high; |
+ } |
+ |
+ if (version_ok) { |
+ // At this point, the names match and there is no version specified |
+ // or the versions also match. |
+ |
+ std::string desc_or_signer(blacklisted.desc_or_signer); |
+ std::string signer_hash, description_hash; |
+ GenerateHash(WideToUTF8(module.digital_signer), &signer_hash); |
+ GenerateHash(WideToUTF8(module.description), &description_hash); |
+ |
+ // If signatures match, we have a winner. |
+ if (!desc_or_signer.empty() && signer_hash == desc_or_signer) |
+ return CONFIRMED_BAD; |
+ |
+ // If description matches and location, then we also have a match. |
+ if (!desc_or_signer.empty() && description_hash == desc_or_signer && |
+ !location_hash.empty() && location_hash == blacklisted.location) { |
+ return CONFIRMED_BAD; |
+ } |
+ |
+ // We are not sure, but it is likely bad. |
+ return SUSPECTED_BAD; |
+ } |
+ } |
+ |
+ return NOT_MATCHED; |
+} |
+ |
+ModuleEnumerator::ModuleEnumerator(EnumerateModulesModel* observer) |
+: observer_(observer) { |
tfarina
2010/11/08 14:57:47
nit: indent 4 spaces.
|
+ CHECK(BrowserThread::GetCurrentThreadIdentifier(&callback_thread_id_)); |
+ DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
+} |
+ |
+ModuleEnumerator::~ModuleEnumerator() { |
+} |
+ |
+void ModuleEnumerator::ScanNow(ModulesVector* list) { |
+ DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
+ enumerated_modules_ = list; |
+ BrowserThread::PostTask( |
+ BrowserThread::FILE, FROM_HERE, |
+ NewRunnableMethod(this, &ModuleEnumerator::ScanOnFileThread)); |
+} |
+ |
+void ModuleEnumerator::ScanOnFileThread() { |
+ enumerated_modules_->clear(); |
+ |
+ // Make sure the path mapping vector is setup so we can collapse paths. |
+ PreparePathMappings(); |
+ |
+ // Get all modules in the current process. |
+ ScopedHandle snap(::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, |
+ ::GetCurrentProcessId())); |
+ if (!snap.Get()) |
+ return; |
+ |
+ // Walk the module list. |
+ MODULEENTRY32 module = { sizeof(module) }; |
+ if (!::Module32First(snap.Get(), &module)) |
+ return; |
+ |
+ do { |
+ // It would be weird to present chrome.exe as a loaded module. |
+ if (_wcsicmp(chrome::kBrowserProcessExecutableName, module.szModule) == 0) |
+ continue; |
+ |
+ Module entry; |
+ entry.type = LOADED_MODULE; |
+ entry.status = NOT_MATCHED; |
+ entry.normalized = false; |
+ entry.location = module.szExePath; |
+ entry.digital_signer = |
+ GetSubjectNameFromDigitalSignature(FilePath(entry.location)); |
+ entry.recommended_action = NONE; |
+ scoped_ptr<FileVersionInfo> version_info( |
+ FileVersionInfo::CreateFileVersionInfo(FilePath(entry.location))); |
+ if (version_info.get()) { |
+ FileVersionInfoWin* version_info_win = |
+ static_cast<FileVersionInfoWin*>(version_info.get()); |
+ |
+ VS_FIXEDFILEINFO* fixed_file_info = version_info_win->fixed_file_info(); |
+ if (fixed_file_info) { |
+ entry.description = version_info_win->file_description(); |
+ entry.version = version_info_win->file_version(); |
+ entry.product_name = version_info_win->product_name(); |
+ } |
+ } |
+ |
+ NormalizeModule(&entry); |
+ CollapsePath(&entry); |
+ enumerated_modules_->push_back(entry); |
+ } while (::Module32Next(snap.Get(), &module)); |
+ |
+ // Add to this list the Winsock LSP DLLs. |
+ WinsockLayeredServiceProviderList layered_providers; |
+ GetWinsockLayeredServiceProviders(&layered_providers); |
+ for (size_t i = 0; i < layered_providers.size(); ++i) { |
+ Module entry; |
+ entry.type = WINSOCK_MODULE_REGISTRATION; |
+ entry.status = NOT_MATCHED; |
+ entry.normalized = false; |
+ entry.location = layered_providers[i].path; |
+ entry.description = layered_providers[i].name; |
+ entry.recommended_action = NONE; |
+ |
+ wchar_t expanded[MAX_PATH]; |
+ DWORD size = ExpandEnvironmentStrings( |
+ entry.location.c_str(), expanded, MAX_PATH); |
+ if (size != 0 && size <= MAX_PATH) { |
+ entry.digital_signer = |
+ GetSubjectNameFromDigitalSignature(FilePath(expanded)); |
+ } |
+ entry.version = base::IntToString16(layered_providers[i].version); |
+ |
+ // Paths have already been collapsed. |
+ NormalizeModule(&entry); |
+ enumerated_modules_->push_back(entry); |
+ } |
+ |
+ MatchAgainstBlacklist(); |
+ |
+ std::sort(enumerated_modules_->begin(), |
+ enumerated_modules_->end(), ModuleSort); |
+ |
+ // Send a reply back on the UI thread. |
+ BrowserThread::PostTask( |
+ callback_thread_id_, FROM_HERE, |
+ NewRunnableMethod(this, &ModuleEnumerator::ReportBack)); |
+} |
+ |
+void ModuleEnumerator::PreparePathMappings() { |
+ path_mapping_.clear(); |
+ |
+ scoped_ptr<base::Environment> environment(base::Environment::Create()); |
+ std::vector<string16> env_vars; |
+ env_vars.push_back(L"LOCALAPPDATA"); |
+ env_vars.push_back(L"ProgramFiles"); |
+ env_vars.push_back(L"USERPROFILE"); |
+ env_vars.push_back(L"SystemRoot"); |
+ env_vars.push_back(L"TEMP"); |
+ for (std::vector<string16>::const_iterator variable = env_vars.begin(); |
+ variable != env_vars.end(); ++variable) { |
+ std::string path; |
+ if (environment->GetVar(WideToASCII(*variable).c_str(), &path)) { |
+ path_mapping_.push_back( |
+ std::make_pair(l10n_util::ToLower(UTF8ToWide(path)) + L"\\", |
+ L"%" + l10n_util::ToLower(*variable) + L"%")); |
+ } |
+ } |
+} |
+ |
+void ModuleEnumerator::CollapsePath(Module* entry) { |
+ // Take the path and see if we can use any of the substitution values |
+ // from the vector constructed above to replace c:\windows with, for |
+ // example, %systemroot%. |
+ for (PathMapping::const_iterator mapping = path_mapping_.begin(); |
+ mapping != path_mapping_.end(); ++mapping) { |
+ string16 prefix = mapping->first; |
+ if (StartsWith(entry->location, prefix, false)) { |
+ entry->location = mapping->second + |
+ entry->location.substr(prefix.length() - 1); |
+ return; |
+ } |
+ } |
+} |
+ |
+void ModuleEnumerator::MatchAgainstBlacklist() { |
+ for (size_t m = 0; m < enumerated_modules_->size(); ++m) { |
+ // Match this module against the blacklist. |
+ Module* module = &(*enumerated_modules_)[m]; |
+ module->status = GOOD; // We change this below potentially. |
+ for (size_t i = 0; i < arraysize(kModuleBlacklist); ++i) { |
+ #if !defined(NDEBUG) |
+ // This saves time when constructing the blacklist. |
+ std::string hashes(kModuleBlacklist[i].filename); |
+ std::string hash1, hash2, hash3; |
+ GenerateHash(kModuleBlacklist[i].filename, &hash1); |
+ hashes += " - " + hash1; |
+ GenerateHash(kModuleBlacklist[i].location, &hash2); |
+ hashes += " - " + hash2; |
+ GenerateHash(kModuleBlacklist[i].desc_or_signer, &hash3); |
+ hashes += " - " + hash3; |
+ #endif |
+ |
+ ModuleStatus status = Match(*module, kModuleBlacklist[i]); |
+ if (status != NOT_MATCHED) { |
+ // We have a match against the blacklist. Mark it as such. |
+ module->status = status; |
+ module->recommended_action = kModuleBlacklist[i].help_tip; |
+ break; |
+ } |
+ } |
+ } |
+} |
+ |
+void ModuleEnumerator::ReportBack() { |
+ DCHECK(BrowserThread::CurrentlyOn(callback_thread_id_)); |
+ observer_->DoneScanning(); |
+} |
+ |
+string16 ModuleEnumerator::GetSubjectNameFromDigitalSignature( |
+ const FilePath& filename) { |
+ HCERTSTORE store = NULL; |
+ HCRYPTMSG message = NULL; |
+ |
+ // Find the crypto message for this filename. |
+ bool result = !!CryptQueryObject(CERT_QUERY_OBJECT_FILE, |
+ filename.value().c_str(), |
+ CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED, |
+ CERT_QUERY_FORMAT_FLAG_BINARY, |
+ 0, |
+ NULL, |
+ NULL, |
+ NULL, |
+ &store, |
+ &message, |
+ NULL); |
+ if (!result) |
+ return string16(); |
+ |
+ // Determine the size of the signer info data. |
+ DWORD signer_info_size = 0; |
+ result = !!CryptMsgGetParam(message, |
+ CMSG_SIGNER_INFO_PARAM, |
+ 0, |
+ NULL, |
+ &signer_info_size); |
+ if (!result) |
+ return string16(); |
+ |
+ // Allocate enough space to hold the signer info. |
+ scoped_array<BYTE> signer_info_buffer(new BYTE[signer_info_size]); |
+ CMSG_SIGNER_INFO* signer_info = |
+ reinterpret_cast<CMSG_SIGNER_INFO*>(signer_info_buffer.get()); |
+ |
+ // Obtain the signer info. |
+ result = !!CryptMsgGetParam(message, |
+ CMSG_SIGNER_INFO_PARAM, |
+ 0, |
+ signer_info, |
+ &signer_info_size); |
+ if (!result) |
+ return string16(); |
+ |
+ // Search for the signer certificate. |
+ CERT_INFO CertInfo = {0}; |
+ PCCERT_CONTEXT cert_context = NULL; |
+ CertInfo.Issuer = signer_info->Issuer; |
+ CertInfo.SerialNumber = signer_info->SerialNumber; |
+ |
+ cert_context = CertFindCertificateInStore( |
+ store, |
+ X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, |
+ 0, |
+ CERT_FIND_SUBJECT_CERT, |
+ &CertInfo, |
+ NULL); |
+ if (!cert_context) |
+ return string16(); |
+ |
+ // Determine the size of the Subject name. |
+ DWORD subject_name_size = 0; |
+ if (!(subject_name_size = CertGetNameString(cert_context, |
+ CERT_NAME_SIMPLE_DISPLAY_TYPE, |
+ 0, |
+ NULL, |
+ NULL, |
+ 0))) { |
+ return string16(); |
+ } |
+ |
+ string16 subject_name; |
+ subject_name.resize(subject_name_size); |
+ |
+ // Get subject name. |
+ if (!(CertGetNameString(cert_context, |
+ CERT_NAME_SIMPLE_DISPLAY_TYPE, |
+ 0, |
+ NULL, |
+ const_cast<LPWSTR>(subject_name.c_str()), |
+ subject_name_size))) { |
+ return string16(); |
+ } |
+ |
+ return subject_name; |
+} |
+ |
+// ---------------------------------------------------------------------------- |
+ |
+void EnumerateModulesModel::ScanNow() { |
+ if (scanning_) |
+ return; // A scan is already in progress. |
+ |
+ lock->Acquire(); // Balanced in DoneScanning(); |
+ |
+ scanning_ = true; |
+ |
+ // Instruct the ModuleEnumerator class to load this on the File thread. |
+ // ScanNow does not block. |
+ if (!module_enumerator_) |
+ module_enumerator_ = new ModuleEnumerator(this); |
+ module_enumerator_->ScanNow(&enumerated_modules_); |
+} |
+ |
+ListValue* EnumerateModulesModel::GetModuleList() { |
+ if (scanning_) |
+ return NULL; |
+ |
+ lock->Acquire(); |
+ |
+ if (enumerated_modules_.empty()) { |
+ lock->Release(); |
+ return NULL; |
+ } |
+ |
+ ListValue* list = new ListValue(); |
+ |
+ for (ModuleEnumerator::ModulesVector::const_iterator module = |
+ enumerated_modules_.begin(); |
+ module != enumerated_modules_.end(); ++module) { |
+ DictionaryValue* data = new DictionaryValue(); |
+ data->SetInteger("type", module->type); |
+ data->SetString("type_description", |
+ (module->type == ModuleEnumerator::WINSOCK_MODULE_REGISTRATION) ? |
+ ASCIIToWide("Winsock") : ASCIIToWide("")); |
+ data->SetInteger("status", module->status); |
+ data->SetString("location", module->location); |
+ data->SetString("name", module->name); |
+ data->SetString("product_name", module->product_name); |
+ data->SetString("description", module->description); |
+ data->SetString("version", module->version.empty() ? ASCIIToWide("") : |
+ l10n_util::GetStringF(IDS_CONFLICTS_CHECK_VERSION_STRING, |
+ module->version)); |
+ data->SetString("digital_signer", module->digital_signer); |
+ |
+ // Figure out the possible resolution help string. |
+ string16 actions; |
+ string16 separator = ASCIIToWide(" ") + l10n_util::GetStringUTF16( |
+ IDS_CONFLICTS_CHECK_POSSIBLE_ACTION_SEPERATOR) + |
+ ASCIIToWide(" "); |
+ |
+ if (module->recommended_action & ModuleEnumerator::NONE) { |
+ actions = l10n_util::GetStringUTF16( |
+ IDS_CONFLICTS_CHECK_INVESTIGATING); |
+ } |
+ if (module->recommended_action & ModuleEnumerator::UNINSTALL) { |
+ if (!actions.empty()) |
+ actions += separator; |
+ actions = l10n_util::GetStringUTF16( |
+ IDS_CONFLICTS_CHECK_POSSIBLE_ACTION_UNINSTALL); |
+ } |
+ if (module->recommended_action & ModuleEnumerator::UPDATE) { |
+ if (!actions.empty()) |
+ actions += separator; |
+ actions += l10n_util::GetStringUTF16( |
+ IDS_CONFLICTS_CHECK_POSSIBLE_ACTION_UPDATE); |
+ } |
+ if (module->recommended_action & ModuleEnumerator::DISABLE) { |
+ if (!actions.empty()) |
+ actions += separator; |
+ actions += l10n_util::GetStringUTF16( |
+ IDS_CONFLICTS_CHECK_POSSIBLE_ACTION_DISABLE); |
+ } |
+ string16 possible_resolution = actions.empty() ? ASCIIToWide("") : |
+ l10n_util::GetStringUTF16(IDS_CONFLICTS_CHECK_POSSIBLE_ACTIONS) + |
+ ASCIIToWide(" ") + |
+ actions; |
+ data->SetString("possibleResolution", possible_resolution); |
+ data->SetString("help_url", ConstructHelpCenterUrl(*module).spec().c_str()); |
+ |
+ list->Append(data); |
+ } |
+ |
+ lock->Release(); |
+ return list; |
+} |
+ |
+EnumerateModulesModel::EnumerateModulesModel() |
+: scanning_(false), |
tfarina
2010/11/08 14:57:47
nit: indent 4 spaces.
|
+confirmed_bad_modules_detected_(0), |
tfarina
2010/11/08 14:57:47
mit: align with |scanning_|.
|
+suspected_bad_modules_detected_(0) { |
+ const CommandLine& cmd_line = *CommandLine::ForCurrentProcess(); |
+ if (cmd_line.HasSwitch(switches::kConflictingModulesCheck)) { |
+ check_modules_timer_.Start( |
+ base::TimeDelta::FromMilliseconds(kModuleCheckDelayMs), |
tfarina
2010/11/08 14:57:47
nit: indent 4 spaces.
|
+ this, &EnumerateModulesModel::ScanNow); |
+ } |
+ |
+ lock = new Lock(); |
+} |
+ |
+EnumerateModulesModel::~EnumerateModulesModel() { |
+ delete lock; |
+} |
+ |
+void EnumerateModulesModel::DoneScanning() { |
+ confirmed_bad_modules_detected_ = 0; |
+ suspected_bad_modules_detected_ = 0; |
+ for (ModuleEnumerator::ModulesVector::const_iterator module = |
+ enumerated_modules_.begin(); |
+ module != enumerated_modules_.end(); ++module) { |
+ if (module->status == ModuleEnumerator::CONFIRMED_BAD) |
+ ++confirmed_bad_modules_detected_; |
+ if (module->status == ModuleEnumerator::SUSPECTED_BAD) |
+ ++suspected_bad_modules_detected_; |
+ } |
+ |
+ scanning_ = false; |
+ lock->Release(); |
+ |
+ NotificationService::current()->Notify( |
+ NotificationType::MODULE_LIST_ENUMERATED, |
+ Source<EnumerateModulesModel>(this), |
+ NotificationService::NoDetails()); |
+ |
+ if (suspected_bad_modules_detected_ || confirmed_bad_modules_detected_) { |
+ bool found_confirmed_bad_modules = confirmed_bad_modules_detected_ > 0; |
+ NotificationService::current()->Notify( |
+ NotificationType::MODULE_INCOMPATIBILITY_DETECTED, |
+ Source<EnumerateModulesModel>(this), |
+ Details<bool>(&found_confirmed_bad_modules)); |
+ } |
+} |
+ |
+GURL EnumerateModulesModel::ConstructHelpCenterUrl( |
+ const ModuleEnumerator::Module& module) { |
+ if (!(module.recommended_action & ModuleEnumerator::SEE_LINK)) |
+ return GURL(); |
+ |
+ // Construct the needed hashes. |
+ std::string filename, location, description, signer; |
+ GenerateHash(WideToUTF8(module.name), &filename); |
+ GenerateHash(WideToUTF8(module.location), &location); |
+ GenerateHash(WideToUTF8(module.description), &description); |
+ GenerateHash(WideToUTF8(module.digital_signer), &signer); |
+ |
+ string16 url = l10n_util::GetStringF(IDS_HELP_CENTER_VIEW_CONFLICTS, |
+ ASCIIToWide(filename), ASCIIToWide(location), |
+ ASCIIToWide(description), ASCIIToWide(signer)); |
+ return GURL(WideToUTF8(url)); |
+} |
Property changes on: chrome\browser\enumerate_modules_model_win.cc |
___________________________________________________________________ |
Added: svn:eol-style |
+ LF |